1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
}
|