-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refresh token rotation #540
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,9 +53,9 @@ type OIDCServer interface { | |
|
||
ClientCredsToken(creds oidc.ClientCredentials) (*jose.JWT, error) | ||
|
||
// RefreshToken takes a previously generated refresh token and returns a new ID token | ||
// RefreshToken takes a previously generated refresh token and returns a new ID token and new refresh token | ||
// if the token is valid. | ||
RefreshToken(creds oidc.ClientCredentials, scopes scope.Scopes, token string) (*jose.JWT, error) | ||
RefreshToken(creds oidc.ClientCredentials, scopes scope.Scopes, token string) (*jose.JWT, string, error) | ||
|
||
KillSession(string) error | ||
|
||
|
@@ -567,34 +567,34 @@ func (s *Server) CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jo | |
return jwt, refreshToken, nil | ||
} | ||
|
||
func (s *Server) RefreshToken(creds oidc.ClientCredentials, scopes scope.Scopes, token string) (*jose.JWT, error) { | ||
func (s *Server) RefreshToken(creds oidc.ClientCredentials, scopes scope.Scopes, token string) (*jose.JWT, string, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are the raw Claims ever used or do they immediately call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Raw claims are encoded immediately here. But the interface OIDCServer uses jose.JWT in more places. Do you prefer to change it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, this is fine. Again, it's something we can clean up later. |
||
ok, err := s.ClientManager.Authenticate(creds) | ||
if err != nil { | ||
log.Errorf("Failed fetching client %s from repo: %v", creds.ID, err) | ||
return nil, oauth2.NewError(oauth2.ErrorServerError) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
if !ok { | ||
log.Errorf("Failed to Authenticate client %s", creds.ID) | ||
return nil, oauth2.NewError(oauth2.ErrorInvalidClient) | ||
return nil, "", oauth2.NewError(oauth2.ErrorInvalidClient) | ||
} | ||
|
||
userID, connectorID, rtScopes, err := s.RefreshTokenRepo.Verify(creds.ID, token) | ||
switch err { | ||
case nil: | ||
break | ||
case refresh.ErrorInvalidToken: | ||
return nil, oauth2.NewError(oauth2.ErrorInvalidRequest) | ||
return nil, "", oauth2.NewError(oauth2.ErrorInvalidRequest) | ||
case refresh.ErrorInvalidClientID: | ||
return nil, oauth2.NewError(oauth2.ErrorInvalidClient) | ||
return nil, "", oauth2.NewError(oauth2.ErrorInvalidClient) | ||
default: | ||
return nil, oauth2.NewError(oauth2.ErrorServerError) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
|
||
if len(scopes) == 0 { | ||
scopes = rtScopes | ||
} else { | ||
if !rtScopes.Contains(scopes) { | ||
return nil, oauth2.NewError(oauth2.ErrorInvalidRequest) | ||
return nil, "", oauth2.NewError(oauth2.ErrorInvalidRequest) | ||
} | ||
} | ||
|
||
|
@@ -603,27 +603,27 @@ func (s *Server) RefreshToken(creds oidc.ClientCredentials, scopes scope.Scopes, | |
// The error can be user.ErrorNotFound, but we are not deleting | ||
// user at this moment, so this shouldn't happen. | ||
log.Errorf("Failed to fetch user %q from repo: %v: ", userID, err) | ||
return nil, oauth2.NewError(oauth2.ErrorServerError) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
|
||
var groups []string | ||
if rtScopes.HasScope(scope.ScopeGroups) { | ||
conn, ok := s.connector(connectorID) | ||
if !ok { | ||
log.Errorf("refresh token contained invalid connector ID (%s)", connectorID) | ||
return nil, oauth2.NewError(oauth2.ErrorServerError) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
|
||
grouper, ok := conn.(connector.GroupsConnector) | ||
if !ok { | ||
log.Errorf("refresh token requested groups for connector (%s) that doesn't support groups", connectorID) | ||
return nil, oauth2.NewError(oauth2.ErrorServerError) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
|
||
remoteIdentities, err := s.UserRepo.GetRemoteIdentities(nil, userID) | ||
if err != nil { | ||
log.Errorf("failed to get remote identities: %v", err) | ||
return nil, oauth2.NewError(oauth2.ErrorServerError) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
remoteIdentity, ok := func() (user.RemoteIdentity, bool) { | ||
for _, ri := range remoteIdentities { | ||
|
@@ -635,18 +635,18 @@ func (s *Server) RefreshToken(creds oidc.ClientCredentials, scopes scope.Scopes, | |
}() | ||
if !ok { | ||
log.Errorf("failed to get remote identity for connector %s", connectorID) | ||
return nil, oauth2.NewError(oauth2.ErrorServerError) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
if groups, err = grouper.Groups(remoteIdentity.ID); err != nil { | ||
log.Errorf("failed to get groups for refresh token: %v", connectorID) | ||
return nil, oauth2.NewError(oauth2.ErrorServerError) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
} | ||
|
||
signer, err := s.KeyManager.Signer() | ||
if err != nil { | ||
log.Errorf("Failed to refresh ID token: %v", err) | ||
return nil, oauth2.NewError(oauth2.ErrorServerError) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
|
||
now := time.Now() | ||
|
@@ -666,12 +666,18 @@ func (s *Server) RefreshToken(creds oidc.ClientCredentials, scopes scope.Scopes, | |
jwt, err := jose.NewSignedJWT(claims, signer) | ||
if err != nil { | ||
log.Errorf("Failed to generate ID token: %v", err) | ||
return nil, oauth2.NewError(oauth2.ErrorServerError) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
|
||
refreshToken, err := s.RefreshTokenRepo.RenewRefreshToken(creds.ID, userID, token) | ||
if err != nil { | ||
log.Errorf("Failed to generate new refresh token: %v", err) | ||
return nil, "", oauth2.NewError(oauth2.ErrorServerError) | ||
} | ||
|
||
log.Infof("New token sent: clientID=%s", creds.ID) | ||
|
||
return jwt, nil | ||
return jwt, refreshToken, nil | ||
} | ||
|
||
func (s *Server) CrossClientAuthAllowed(requestingClientID, authorizingClientID string) (bool, error) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is verify outside the transaction?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because if dex can't verify the token you save opening a transaction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay sure