...
1
2
3 package oauth
4
5 import (
6 "context"
7 "errors"
8 "sort"
9
10 "github.com/twmb/franz-go/pkg/sasl"
11 )
12
13
14
15
16
17 type Auth struct {
18
19 Zid string
20
21
22
23 Token string
24
25 Extensions map[string]string
26
27 _ struct{}
28 }
29
30
31
32
33
34
35 func (a Auth) AsMechanism() sasl.Mechanism {
36 return Oauth(func(context.Context) (Auth, error) {
37 return a, nil
38 })
39 }
40
41
42
43 func Oauth(authFn func(context.Context) (Auth, error)) sasl.Mechanism {
44 return oauth(authFn)
45 }
46
47 type oauth func(context.Context) (Auth, error)
48
49 func (oauth) Name() string { return "OAUTHBEARER" }
50 func (fn oauth) Authenticate(ctx context.Context, _ string) (sasl.Session, []byte, error) {
51 auth, err := fn(ctx)
52 if err != nil {
53 return nil, nil, err
54 }
55 if auth.Token == "" {
56 return nil, nil, errors.New("OAUTHBEARER token must be non-empty")
57 }
58
59
60 type kv struct {
61 k string
62 v string
63 }
64 kvs := make([]kv, 0, len(auth.Extensions))
65 for k, v := range auth.Extensions {
66 if len(k) == 0 {
67 continue
68 }
69 kvs = append(kvs, kv{k, v})
70 }
71 sort.Slice(kvs, func(i, j int) bool { return kvs[i].k < kvs[j].k })
72
73
74 gs2Header := "n,"
75 if auth.Zid != "" {
76 gs2Header += "a=" + auth.Zid
77 }
78 gs2Header += ","
79 init := []byte(gs2Header + "\x01auth=Bearer ")
80 init = append(init, auth.Token...)
81 init = append(init, '\x01')
82 for _, kv := range kvs {
83 init = append(init, kv.k...)
84 init = append(init, '=')
85 init = append(init, kv.v...)
86 init = append(init, '\x01')
87 }
88 init = append(init, '\x01')
89
90 return session{}, init, nil
91 }
92
93 type session struct{}
94
95 func (session) Challenge(resp []byte) (bool, []byte, error) {
96 if len(resp) != 0 {
97 return false, nil, errors.New("unexpected data in oauth response")
98 }
99 return true, nil, nil
100 }
101
View as plain text