aboutsummaryrefslogtreecommitdiff
path: root/backend/auth/dpop.go
blob: 2a8f6edf5b2a9c26d19695f1ac40f44b6c0c6e90 (plain)
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
}