Skip to content

Commit 0cc22f0

Browse files
committed
fix(oauth): bind refresh grants to authenticated client
1 parent a6c6f16 commit 0cc22f0

1 file changed

Lines changed: 14 additions & 15 deletions

File tree

oauth/api/token.go

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -191,55 +191,54 @@ func handleRefreshTokenExchange(c *gin.Context) {
191191
return
192192
}
193193

194-
entityID, _ := claims["sub"].(string)
195-
scope, _ := claims["scope"].(string)
196-
197-
if !service.ScopesContain(scope, "refresh_token") {
194+
refreshClaims, err := parseRefreshTokenClaims(claims, clientID)
195+
if err != nil {
198196
c.JSON(http.StatusUnauthorized, gin.H{"error": "provided token is not a refresh token"})
199197
return
200198
}
201199

202-
// Revoke the old refresh token
203-
if tokenID, ok := claims["jti"].(string); ok {
204-
sentinel.Delete("/api/core/token/"+tokenID, nil)
200+
if err := sentinel.Delete("/api/core/token/"+refreshClaims.TokenID, nil); err != nil {
201+
logger.SugarLogger.Errorf("Failed to revoke OAuth refresh token %s: %v", refreshClaims.TokenID, err)
202+
c.JSON(http.StatusBadGateway, gin.H{"error": "failed to rotate refresh token"})
203+
return
205204
}
206205

207206
// Re-check the gate on refresh — group membership may have changed
208207
// since the original grant. If the user no longer qualifies, the
209208
// refresh fails and they have to re-authenticate (which will hit the
210209
// gate again at the authorize step).
211-
if err := service.CheckAccessGate(entityID, clientID); err != nil {
210+
if err := service.CheckAccessGate(refreshClaims.EntityID, clientID); err != nil {
212211
writeGateError(c, err)
213212
return
214213
}
215214

216215
// Strip refresh_token from scope for the access token
217-
accessScope := service.RemoveScope(scope, "refresh_token")
216+
accessScope := service.RemoveScope(refreshClaims.Scope, "refresh_token")
218217

219-
newClaims, err := service.BuildTokenClaims(entityID, clientID, accessScope)
218+
newClaims, err := service.BuildTokenClaims(refreshClaims.EntityID, clientID, accessScope)
220219
if err != nil {
221220
logger.SugarLogger.Errorf("Failed to build token claims: %v", err)
222221
c.JSON(http.StatusBadGateway, gin.H{"error": "server_error"})
223222
return
224223
}
225224

226225
// Generate new access token
227-
accessToken, accessTokenID, err := generateToken(entityID, clientID, accessScope, config.AccessTokenTTL, newClaims)
226+
accessToken, accessTokenID, err := generateToken(refreshClaims.EntityID, clientID, accessScope, config.AccessTokenTTL, newClaims)
228227
if err != nil {
229228
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate access token"})
230229
return
231230
}
232231

233232
// Generate new refresh token (keep refresh_token in scope)
234-
newRefreshToken, newRefreshTokenID, err := generateToken(entityID, clientID, scope, config.RefreshTokenTTL, newClaims)
233+
newRefreshToken, newRefreshTokenID, err := generateToken(refreshClaims.EntityID, clientID, refreshClaims.Scope, config.RefreshTokenTTL, newClaims)
235234
if err != nil {
236235
logger.SugarLogger.Errorf("Failed to generate refresh token: %v", err)
237236
newRefreshToken = ""
238237
newRefreshTokenID = ""
239238
}
240239

241240
sentinel.Post("/api/core/entity/logins", map[string]string{
242-
"entity_id": entityID,
241+
"entity_id": refreshClaims.EntityID,
243242
"client_id": clientID,
244243
"scope": accessScope,
245244
"access_token_id": accessTokenID,
@@ -253,13 +252,13 @@ func handleRefreshTokenExchange(c *gin.Context) {
253252
// carried forward.
254253
var idToken string
255254
if service.ScopesContain(accessScope, "openid") {
256-
idClaims, idErr := service.BuildIDTokenClaims(entityID, clientID, accessScope, "", accessToken, time.Now().Unix())
255+
idClaims, idErr := service.BuildIDTokenClaims(refreshClaims.EntityID, clientID, accessScope, "", accessToken, time.Now().Unix())
257256
if idErr != nil {
258257
logger.SugarLogger.Errorf("Failed to build id token claims: %v", idErr)
259258
c.JSON(http.StatusBadGateway, gin.H{"error": "server_error"})
260259
return
261260
}
262-
idToken, _, err = generateToken(entityID, clientID, accessScope, config.AccessTokenTTL, idClaims)
261+
idToken, _, err = generateToken(refreshClaims.EntityID, clientID, accessScope, config.AccessTokenTTL, idClaims)
263262
if err != nil {
264263
logger.SugarLogger.Errorf("Failed to generate id token: %v", err)
265264
idToken = ""

0 commit comments

Comments
 (0)