1
21
22 package oauth2
23
24 import (
25 "context"
26 "fmt"
27 "strings"
28 "time"
29
30 "github.com/ory/x/errorsx"
31
32 "github.com/pkg/errors"
33
34 "github.com/ory/fosite"
35 "github.com/ory/fosite/storage"
36 )
37
38 type RefreshTokenGrantHandler struct {
39 AccessTokenStrategy AccessTokenStrategy
40 RefreshTokenStrategy RefreshTokenStrategy
41 TokenRevocationStorage TokenRevocationStorage
42
43
44 AccessTokenLifespan time.Duration
45
46
47 RefreshTokenLifespan time.Duration
48
49 ScopeStrategy fosite.ScopeStrategy
50 AudienceMatchingStrategy fosite.AudienceMatchingStrategy
51 RefreshTokenScopes []string
52 }
53
54
55 func (c *RefreshTokenGrantHandler) HandleTokenEndpointRequest(ctx context.Context, request fosite.AccessRequester) error {
56 if !c.CanHandleTokenEndpointRequest(request) {
57 return errorsx.WithStack(fosite.ErrUnknownRequest)
58 }
59
60 if !request.GetClient().GetGrantTypes().Has("refresh_token") {
61 return errorsx.WithStack(fosite.ErrUnauthorizedClient.WithHint("The OAuth 2.0 Client is not allowed to use authorization grant 'refresh_token'."))
62 }
63
64 refresh := request.GetRequestForm().Get("refresh_token")
65 signature := c.RefreshTokenStrategy.RefreshTokenSignature(refresh)
66 originalRequest, err := c.TokenRevocationStorage.GetRefreshTokenSession(ctx, signature, request.GetSession())
67 if errors.Is(err, fosite.ErrInactiveToken) {
68
69 if rErr := c.handleRefreshTokenReuse(ctx, signature, originalRequest); rErr != nil {
70 return errorsx.WithStack(fosite.ErrServerError.WithWrap(rErr).WithDebug(rErr.Error()))
71 }
72
73 return errorsx.WithStack(fosite.ErrInactiveToken.WithWrap(err).WithDebug(err.Error()))
74 } else if errors.Is(err, fosite.ErrNotFound) {
75 return errorsx.WithStack(fosite.ErrInvalidGrant.WithWrap(err).WithDebugf("The refresh token has not been found: %s", err.Error()))
76 } else if err != nil {
77 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
78 } else if err := c.RefreshTokenStrategy.ValidateRefreshToken(ctx, originalRequest, refresh); err != nil {
79
80
81 if errors.Is(err, fosite.ErrTokenExpired) {
82 return errorsx.WithStack(fosite.ErrInvalidGrant.WithWrap(err).WithDebug(err.Error()))
83 }
84 return errorsx.WithStack(fosite.ErrInvalidRequest.WithWrap(err).WithDebug(err.Error()))
85 }
86
87 if !(len(c.RefreshTokenScopes) == 0 || originalRequest.GetGrantedScopes().HasOneOf(c.RefreshTokenScopes...)) {
88 scopeNames := strings.Join(c.RefreshTokenScopes, " or ")
89 hint := fmt.Sprintf("The OAuth 2.0 Client was not granted scope %s and may thus not perform the 'refresh_token' authorization grant.", scopeNames)
90 return errorsx.WithStack(fosite.ErrScopeNotGranted.WithHint(hint))
91
92 }
93
94
95 if originalRequest.GetClient().GetID() != request.GetClient().GetID() {
96 return errorsx.WithStack(fosite.ErrInvalidGrant.WithHint("The OAuth 2.0 Client ID from this request does not match the ID during the initial token issuance."))
97 }
98
99 request.SetSession(originalRequest.GetSession().Clone())
100 request.SetRequestedScopes(originalRequest.GetRequestedScopes())
101 request.SetRequestedAudience(originalRequest.GetRequestedAudience())
102
103 for _, scope := range originalRequest.GetGrantedScopes() {
104 if !c.ScopeStrategy(request.GetClient().GetScopes(), scope) {
105 return errorsx.WithStack(fosite.ErrInvalidScope.WithHintf("The OAuth 2.0 Client is not allowed to request scope '%s'.", scope))
106 }
107 request.GrantScope(scope)
108 }
109
110 if err := c.AudienceMatchingStrategy(request.GetClient().GetAudience(), originalRequest.GetGrantedAudience()); err != nil {
111 return err
112 }
113
114 for _, audience := range originalRequest.GetGrantedAudience() {
115 request.GrantAudience(audience)
116 }
117
118 request.GetSession().SetExpiresAt(fosite.AccessToken, time.Now().UTC().Add(c.AccessTokenLifespan).Round(time.Second))
119 if c.RefreshTokenLifespan > -1 {
120 request.GetSession().SetExpiresAt(fosite.RefreshToken, time.Now().UTC().Add(c.RefreshTokenLifespan).Round(time.Second))
121 }
122
123 return nil
124 }
125
126
127 func (c *RefreshTokenGrantHandler) PopulateTokenEndpointResponse(ctx context.Context, requester fosite.AccessRequester, responder fosite.AccessResponder) (err error) {
128 if !c.CanHandleTokenEndpointRequest(requester) {
129 return errorsx.WithStack(fosite.ErrUnknownRequest)
130 }
131
132 accessToken, accessSignature, err := c.AccessTokenStrategy.GenerateAccessToken(ctx, requester)
133 if err != nil {
134 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
135 }
136
137 refreshToken, refreshSignature, err := c.RefreshTokenStrategy.GenerateRefreshToken(ctx, requester)
138 if err != nil {
139 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
140 }
141
142 signature := c.RefreshTokenStrategy.RefreshTokenSignature(requester.GetRequestForm().Get("refresh_token"))
143
144 ctx, err = storage.MaybeBeginTx(ctx, c.TokenRevocationStorage)
145 if err != nil {
146 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
147 }
148 defer func() {
149 err = c.handleRefreshTokenEndpointStorageError(ctx, err)
150 }()
151
152 ts, err := c.TokenRevocationStorage.GetRefreshTokenSession(ctx, signature, nil)
153 if err != nil {
154 return err
155 } else if err := c.TokenRevocationStorage.RevokeAccessToken(ctx, ts.GetID()); err != nil {
156 return err
157 }
158
159 if err := c.TokenRevocationStorage.RevokeRefreshTokenMaybeGracePeriod(ctx, ts.GetID(), signature); err != nil {
160 return err
161 }
162
163 storeReq := requester.Sanitize([]string{})
164 storeReq.SetID(ts.GetID())
165
166 if err = c.TokenRevocationStorage.CreateAccessTokenSession(ctx, accessSignature, storeReq); err != nil {
167 return err
168 }
169
170 if err = c.TokenRevocationStorage.CreateRefreshTokenSession(ctx, refreshSignature, storeReq); err != nil {
171 return err
172 }
173
174 responder.SetAccessToken(accessToken)
175 responder.SetTokenType("bearer")
176 responder.SetExpiresIn(getExpiresIn(requester, fosite.AccessToken, c.AccessTokenLifespan, time.Now().UTC()))
177 responder.SetScopes(requester.GetGrantedScopes())
178 responder.SetExtra("refresh_token", refreshToken)
179
180 if err = storage.MaybeCommitTx(ctx, c.TokenRevocationStorage); err != nil {
181 return err
182 }
183
184 return nil
185 }
186
187
188
189
190
191
192
193
194
195
196
197 func (c *RefreshTokenGrantHandler) handleRefreshTokenReuse(ctx context.Context, signature string, req fosite.Requester) (err error) {
198 ctx, err = storage.MaybeBeginTx(ctx, c.TokenRevocationStorage)
199 if err != nil {
200 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
201 }
202 defer func() {
203 err = c.handleRefreshTokenEndpointStorageError(ctx, err)
204 }()
205
206 if err = c.TokenRevocationStorage.DeleteRefreshTokenSession(ctx, signature); err != nil {
207 return err
208 } else if err = c.TokenRevocationStorage.RevokeRefreshToken(
209 ctx, req.GetID(),
210 ); err != nil && !errors.Is(err, fosite.ErrNotFound) {
211 return err
212 } else if err = c.TokenRevocationStorage.RevokeAccessToken(
213 ctx, req.GetID(),
214 ); err != nil && !errors.Is(err, fosite.ErrNotFound) {
215 return err
216 }
217
218 if err = storage.MaybeCommitTx(ctx, c.TokenRevocationStorage); err != nil {
219 return err
220 }
221
222 return nil
223 }
224
225 func (c *RefreshTokenGrantHandler) handleRefreshTokenEndpointStorageError(ctx context.Context, storageErr error) (err error) {
226 if storageErr == nil {
227 return nil
228 }
229
230 defer func() {
231 if rollBackTxnErr := storage.MaybeRollbackTx(ctx, c.TokenRevocationStorage); rollBackTxnErr != nil {
232 err = errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebugf("error: %s; rollback error: %s", err, rollBackTxnErr))
233 }
234 }()
235
236 if errors.Is(storageErr, fosite.ErrSerializationFailure) {
237 return errorsx.WithStack(fosite.ErrInvalidRequest.
238 WithDebugf(storageErr.Error()).
239 WithHint("Failed to refresh token because of multiple concurrent requests using the same token which is not allowed."))
240 }
241
242 if errors.Is(storageErr, fosite.ErrNotFound) || errors.Is(storageErr, fosite.ErrInactiveToken) {
243 return errorsx.WithStack(fosite.ErrInvalidRequest.
244 WithDebugf(storageErr.Error()).
245 WithHint("Failed to refresh token because of multiple concurrent requests using the same token which is not allowed."))
246 }
247
248 return errorsx.WithStack(fosite.ErrServerError.WithWrap(storageErr).WithDebug(storageErr.Error()))
249 }
250
251 func (c *RefreshTokenGrantHandler) CanSkipClientAuth(requester fosite.AccessRequester) bool {
252 return false
253 }
254
255 func (c *RefreshTokenGrantHandler) CanHandleTokenEndpointRequest(requester fosite.AccessRequester) bool {
256
257
258 return requester.GetGrantTypes().ExactOne("refresh_token")
259 }
260
View as plain text