diff options
| author | Guillermo Serrahima <dev@vylion.com> | 2026-04-11 07:40:30 +0200 |
|---|---|---|
| committer | Guillermo Serrahima <dev@vylion.com> | 2026-04-11 23:39:19 +0200 |
| commit | 9f0a9a6779f00786e4728ad5c3fad36814a293b0 (patch) | |
| tree | 4b16d535c5d41ef46980608a88b7581be3c2bfd0 /auth/dpop.go | |
| parent | f6d60077b7cfc0d589fa7961e2bb59aea9f993a3 (diff) | |
| download | llostre-9f0a9a6779f00786e4728ad5c3fad36814a293b0.tar.xz llostre-9f0a9a6779f00786e4728ad5c3fad36814a293b0.zip | |
project restructure
Diffstat (limited to 'auth/dpop.go')
| -rw-r--r-- | auth/dpop.go | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/auth/dpop.go b/auth/dpop.go new file mode 100644 index 0000000..2a8f6ed --- /dev/null +++ b/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 +} |
