...
1 package barcode
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "time"
8
9 "github.com/ory/fosite"
10 "github.com/ory/fosite/token/hmac"
11 "github.com/ory/x/errorsx"
12 "golang.org/x/crypto/bcrypt"
13
14 "edge-infra.dev/pkg/edge/iam/config"
15 iamErrors "edge-infra.dev/pkg/edge/iam/errors"
16 "edge-infra.dev/pkg/edge/iam/prometheus"
17 "edge-infra.dev/pkg/edge/iam/session"
18 "edge-infra.dev/pkg/edge/iam/util"
19 )
20
21
22
23 type CodeGrantHandler struct {
24 BarcodeStrategy *OpaqueStrategy
25 SignedStrategy *SignedStrategy
26 BarcodeStorage Storage
27 BarcodeCodeHMACStrategy *hmac.HMACStrategy
28
29
30 Metrics *prometheus.Metrics
31 }
32
33
34 func (h *CodeGrantHandler) CanHandleTokenEndpointRequest(requester fosite.AccessRequester) bool {
35 return requester.GetGrantTypes().ExactOne("barcode_code")
36 }
37
38
39 func (h *CodeGrantHandler) CanSkipClientAuth(_ fosite.AccessRequester) bool {
40 return false
41 }
42
43
44 func (h *CodeGrantHandler) HandleTokenEndpointRequest(ctx context.Context, requester fosite.AccessRequester) error {
45
46 if !h.CanHandleTokenEndpointRequest(requester) {
47 return errorsx.WithStack(fosite.ErrUnknownRequest)
48 }
49 h.Metrics.IncHTTPRequestsTotal(signUpBarcode)
50
51
52 client := requester.GetClient()
53
54
55 if !client.GetGrantTypes().Has("barcode") {
56 return errorsx.WithStack(fosite.ErrUnauthorizedClient.
57 WithHint("The OAuth 2.0 Client is not allowed to use authorization grant 'barcode'."))
58 }
59
60
61 if client.IsPublic() {
62 return errorsx.WithStack(fosite.ErrInvalidGrant.
63 WithHint("The OAuth 2.0 Client is marked as public and is thus not allowed to use authorization grant 'barcode'."))
64 }
65
66
67 code := requester.GetRequestForm().Get("code")
68 signature := h.BarcodeCodeHMACStrategy.Signature(code)
69
70 codeData, err := h.BarcodeStorage.GetBarcodeCode(ctx, signature)
71 if err != nil {
72
73 if errors.Is(err, iamErrors.ErrUsedBarcodeCode) {
74 return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint(err.Error()))
75 }
76
77 return errorsx.WithStack(fosite.ErrServerError.WithHint(err.Error()))
78 }
79
80
81 err = h.BarcodeCodeHMACStrategy.Validate(code)
82 if err != nil {
83 return errorsx.WithStack(fosite.ErrInvalidGrant.WithWrap(err).WithDebug(err.Error()))
84 }
85
86
87 expires := codeData.CreatedAt.UTC().Add(config.GetBarcodeCodeLifespan())
88 if expires.Before(time.Now().UTC()) {
89
90
91
92
93 err := h.BarcodeStorage.DeleteBarcodeCode(ctx, code)
94 if err != nil {
95 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
96 }
97
98
99 return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint("received expired/invalid code"))
100 }
101
102
103
104
105
106
107 if codeData.ClientID != client.GetID() {
108
109 err := h.BarcodeStorage.DeleteBarcodeCode(ctx, code)
110 if err != nil {
111 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
112 }
113 return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint("you are not the right client"))
114 }
115
116
117 subject := codeData.Subject
118 session.FromRequester(requester).SetSubject(subject)
119
120 return nil
121 }
122
123 func (h *CodeGrantHandler) PopulateTokenEndpointResponse(ctx context.Context, requester fosite.AccessRequester, responder fosite.AccessResponder) (err error) {
124
125 if !h.CanHandleTokenEndpointRequest(requester) {
126 return errorsx.WithStack(fosite.ErrUnknownRequest)
127 }
128 signature := h.BarcodeCodeHMACStrategy.Signature(requester.GetRequestForm().Get("code"))
129 barCodeCode, err := h.BarcodeStorage.GetBarcodeCode(ctx, signature)
130 if err != nil {
131 return errorsx.WithStack(fosite.ErrServerError.WithHint(err.Error()))
132 }
133 var barcode string
134 switch barCodeCode.Type {
135 case "qr":
136 barcode, err = h.SignedStrategy.Generate(barCodeCode.Subject, barCodeCode.IssuedBy)
137 if err != nil {
138 return errorsx.WithStack(fosite.ErrServerError.WithHint("failed to generate encoded token for QR barcode"))
139 }
140 case "128A":
141 barcode, err = h.BarcodeStrategy.Generate()
142 if err != nil {
143 return errorsx.WithStack(fosite.ErrServerError.WithHint("failed to generate encoded token for 128A barcode"))
144 }
145 subject := session.FromRequester(requester).GetSubject()
146 key := h.BarcodeStrategy.GetKey(barcode)
147
148 if err = h.BarcodeStorage.CreateBarcodeKey(ctx, barCodeCode.Challenge, key); err != nil {
149 return errorsx.WithStack(fosite.ErrServerError.WithHintf("failed to create barcode key: %v", err.Error()))
150 }
151
152
153 if _, err := h.BarcodeStorage.GetBarcodeUser(ctx, subject); err != nil {
154 if err = h.BarcodeStorage.CreateBarcodeUser(ctx, subject, ""); err != nil {
155 return errorsx.WithStack(fosite.ErrServerError.WithHintf("failed to create barcode user: %v", err.Error()))
156 }
157 }
158 barcodePrefixLen := uint8(len(config.BarcodePrefix()))
159 secret := barcode[barcodePrefixLen+config.GetBarcodeKeyLength():]
160 hash, _ := bcrypt.GenerateFromPassword([]byte(secret), config.BcryptCost())
161
162 if err := h.BarcodeStorage.CreateBarcode(ctx, key, string(hash), subject); err != nil {
163 return fmt.Errorf("failed to create barcode 128A: %w", err)
164 }
165 }
166
167
168 err = h.BarcodeStorage.InvalidateBarcodeCode(ctx, signature, barCodeCode.Subject, barCodeCode.Subject, barCodeCode.ClientID, barCodeCode.Type, barCodeCode.Challenge)
169 if err != nil {
170 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()).WithHint("failed to invalidate barcode code"))
171 }
172
173
174 responder.SetAccessToken(barcode)
175 responder.SetTokenType(barCodeCode.Type)
176 h.Metrics.IncSignUpRequestsTotal(signUpBarcode, util.Succeeded)
177 return nil
178 }
179
View as plain text