aboutsummaryrefslogtreecommitdiff
path: root/backend/auth/dpop.go
diff options
context:
space:
mode:
Diffstat (limited to 'backend/auth/dpop.go')
-rw-r--r--backend/auth/dpop.go83
1 files changed, 83 insertions, 0 deletions
diff --git a/backend/auth/dpop.go b/backend/auth/dpop.go
new file mode 100644
index 0000000..2a8f6ed
--- /dev/null
+++ b/backend/auth/dpop.go
@@ -0,0 +1,83 @@
+package auth
+
+import (
+ "crypto"
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "encoding/base64"
+ "time"
+
+ "github.com/golang-jwt/jwt/v5"
+)
+
+type DPoPKeyPair struct {
+ PrivateKey *ecdsa.PrivateKey
+ PublicKey *ecdsa.PublicKey
+ JWK map[string]string
+}
+
+func GenerateDPoPKeyPair() (*DPoPKeyPair, error) {
+ privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ return nil, err
+ }
+
+ jwk := map[string]string{
+ "kty": "EC",
+ "crv": "P-256",
+ "x": base64.RawURLEncoding.EncodeToString(privateKey.PublicKey.X.Bytes()),
+ "y": base64.RawURLEncoding.EncodeToString(privateKey.PublicKey.Y.Bytes()),
+ }
+
+ return &DPoPKeyPair{
+ PrivateKey: privateKey,
+ PublicKey: &privateKey.PublicKey,
+ JWK: jwk,
+ }, nil
+}
+
+func (kp *DPoPKeyPair) GenerateDPoPProof(method, url, nonce string) (string, error) {
+ // generate unique ID for this proof
+ jti, err := GenerateRandomToken()
+ if err != nil {
+ return "", err
+ }
+
+ // create JWT header with JWK
+ header := map[string]interface{}{
+ "typ": "dpop+jwt",
+ "alg": "ES256",
+ "jwk": kp.JWK,
+ }
+
+ // create JWT claims
+ claims := jwt.MapClaims{
+ "jti": jti,
+ "htm": method, // HTTP method (GET, POST, etc)
+ "htu": url, // HTTP URL (the endpoint you're calling)
+ "iat": time.Now().Unix(),
+ }
+
+ // add nonce if provided
+ if nonce != "" {
+ claims["nonce"] = nonce
+ }
+
+ // create token with custom header
+ token := jwt.NewWithClaims(&jwt.SigningMethodECDSA{
+ Name: "ES256",
+ Hash: crypto.SHA256,
+ KeySize: 32,
+ CurveBits: 256,
+ }, claims)
+ token.Header = header
+
+ // sign with private key
+ tokenString, err := token.SignedString(kp.PrivateKey)
+ if err != nil {
+ return "", err
+ }
+
+ return tokenString, nil
+}