Live video on the AT Protocol
1package oproxy
2
3import (
4 "crypto/sha256"
5 "encoding/base64"
6 "errors"
7 "fmt"
8 "strings"
9
10 "github.com/AxisCommunications/go-dpop"
11 "github.com/golang-jwt/jwt/v5"
12 "github.com/google/uuid"
13)
14
15func boolPtr(b bool) *bool {
16 return &b
17}
18
19func codeUUID(prefix string) string {
20 uu, err := uuid.NewV7()
21 if err != nil {
22 panic(err)
23 }
24 return fmt.Sprintf("%s-%s", prefix, uu.String())
25}
26
27var urnPrefix = "urn:ietf:params:oauth:request_uri:"
28
29const UUID_LENGTH = 37
30
31func makeURN(jkt string) string {
32 uu, err := uuid.NewV7()
33 if err != nil {
34 panic(err)
35 }
36 return fmt.Sprintf("%s%s-%s", urnPrefix, uu.String(), jkt)
37}
38
39// urn --> jkt, uu
40func parseURN(urn string) (string, string, error) {
41 if !strings.HasPrefix(urn, urnPrefix) {
42 return "", "", fmt.Errorf("invalid URN: %s", urn)
43 }
44 withoutPrefix := urn[len(urnPrefix):]
45 uu := withoutPrefix[:UUID_LENGTH]
46 suffix := withoutPrefix[UUID_LENGTH:]
47 return suffix, uu, nil
48}
49
50func makeState(jkt string) string {
51 uu, err := uuid.NewV7()
52 if err != nil {
53 panic(err)
54 }
55 return fmt.Sprintf("%s-%s", uu.String(), jkt)
56}
57
58func parseState(state string) (string, string, error) {
59 if len(state) < UUID_LENGTH {
60 return "", "", fmt.Errorf("invalid state: %s", state)
61 }
62 uu := state[:UUID_LENGTH]
63 suffix := state[UUID_LENGTH:]
64 return suffix, uu, nil
65}
66
67func makeNonce() string {
68 uu, err := uuid.NewV7()
69 if err != nil {
70 panic(err)
71 }
72 return fmt.Sprintf("nonce-%s", uu.String())
73}
74
75// returns jkt, nonce, error
76func getJKT(dpopJWT string) (string, string, error) {
77 var claims dpop.ProofTokenClaims
78 token, err := jwt.ParseWithClaims(dpopJWT, &claims, keyFunc)
79 if err != nil {
80 return "", "", err
81 }
82 jwk, ok := token.Header["jwk"].(map[string]any)
83 if !ok {
84 return "", "", fmt.Errorf("missing jwk in DPoP JWT header")
85 }
86 jwkJSONbytes, err := getThumbprintableJwkJSONbytes(jwk)
87 if err != nil {
88 // keyFunc used with parseWithClaims should ensure that this can not happen but better safe than sorry.
89 return "", "", errors.Join(dpop.ErrInvalidProof, err)
90 }
91 h := sha256.New()
92 _, err = h.Write(jwkJSONbytes)
93 if err != nil {
94 return "", "", errors.Join(dpop.ErrInvalidProof, err)
95 }
96 b64URLjwkHash := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
97
98 return b64URLjwkHash, claims.Nonce, nil
99}