Skip to content
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

Renew tokens in Credentials Manager if either token has expired [SDK-999] #319

Merged
merged 4 commits into from
Oct 31, 2019

Conversation

cocojoe
Copy link
Member

@cocojoe cocojoe commented Oct 24, 2019

Changes

There are no changes to public API, changes are internal.
Existing behaviour was to validate only the expires_in as the use case was for renewing the AT. However, this has been expanded now to include the ID Token exp as part of the validation.

So if either the AT or IDT are expired then the credentials will be invalid and have to be renewed.

References

#309

Testing

Please describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. If this library has unit and/or integration testing, tests should be added for new functionality and existing tests should complete without errors.

  • This change adds unit test coverage
  • This change has been tested on the latest version of the platform/language or why not

Checklist

Credentials Manager will now refresh upon either token expiring
@cocojoe cocojoe added this to the vNext milestone Oct 24, 2019
@cocojoe cocojoe requested a review from a team October 24, 2019 10:19
@@ -150,11 +150,11 @@ class SafariWebAuth: WebAuth {
func newSafari(_ authorizeURL: URL, callback: @escaping (Result<Credentials>) -> Void) -> (SFSafariViewController, (Result<Credentials>) -> Void) {
let controller = SFSafariViewController(url: authorizeURL)
controller.modalPresentationStyle = safariPresentationStyle

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SwiftLinter picked these up.

Copy link
Contributor

@joshcanhelp joshcanhelp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor stuff 👍

Auth0/CredentialsManager.swift Show resolved Hide resolved
Auth0/CredentialsManager.swift Outdated Show resolved Hide resolved
Auth0/CredentialsManager.swift Outdated Show resolved Hide resolved
Auth0/CredentialsManager.swift Outdated Show resolved Hide resolved
Auth0/CredentialsManager.swift Outdated Show resolved Hide resolved
Auth0/CredentialsManager.swift Outdated Show resolved Hide resolved
Auth0/CredentialsManager.swift Outdated Show resolved Hide resolved
@cocojoe
Copy link
Member Author

cocojoe commented Oct 25, 2019

@joshcanhelp turns out the logic was actually a little broken, I cleaned up naming a little.

@@ -193,4 +192,44 @@ public struct CredentialsManager {
}
}
}

func hasExpired(_ credentials: Credentials) -> Bool {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check logic here please, basically if either token has expired then it should return true

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Current logic looks correct (and tests should verify) ... but we could be much more clear here if we returned on the expired condition instead of setting variables:

if credentials.expiresIn <= Date() { 
    return true
}

var tokenDecoded = decode(jwt: credentials.idToken);
var exp = tokenDecoded["exp"] as? Double;

if Date(timeIntervalSince1970: exp) < Date() { 
    return true
}

return false

That's unlikely to be correct (let alone idiomatic) Swift 😆 but hopefully the message is clear.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cocojoe The hasExpired logic is correct, as josh pointed out. But I think the performance impact could be reduced a lot if instead of checking the exp date of each token and decode the id token every time you call this method, you just save the exp date most close to expire at "save credentials" step, and then just have a single date comparison check on this function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I had similar logic to above but I think a mix up in tests sent me down the wrong path, have corrected now.

I don't mind the idea of a single expiry time based upon lowest. Will leave it for a future internal refactor though.

guard let expiresIn = credentials.expiresIn else { return callback(.noCredentials, nil) }
guard expiresIn < Date() else { return callback(nil, credentials) }
guard credentials.expiresIn != nil else { return callback(.noCredentials, nil) }
guard self.hasExpired(credentials) else { return callback(nil, credentials) }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it has expired continue executing else return credentials we have stored

else { return false }
return expiresIn > Date() || credentials.refreshToken != nil
return !self.hasExpired(credentials) || credentials.refreshToken != nil
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if neither AT or IDT have expired OR we have a refresh token as we can use that to gain new tokens so technically we have valid credentials. Although the RT could be invalid, not possible to know until calling renew.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we be using a refresh token for a new ID token? Particularly in the case of a native app.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joshcanhelp To obtain a fresh copy of the user profile, I imagine. Either that or use the access token to call /userinfo and obtain it that way.

@cocojoe
Copy link
Member Author

cocojoe commented Oct 25, 2019

@lbalmaceda please check logic aligns with your PR thanks

Copy link
Contributor

@lbalmaceda lbalmaceda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic aligns with android. All good 👍

@joshcanhelp joshcanhelp dismissed their stale review October 30, 2019 15:13

Reviewed by Lucho

@cocojoe cocojoe merged commit af14f7a into master Oct 31, 2019
@cocojoe cocojoe mentioned this pull request Oct 31, 2019
@cocojoe cocojoe deleted the added-exp-check-credentials-manager branch November 24, 2019 14:55
sam-w pushed a commit to LoungeBuddy/Auth0.swift that referenced this pull request Mar 11, 2020
…999] (auth0#319)

* Added private token expiry validation method
Credentials Manager will now refresh upon either token expiring

* Moved decode method as unaccessible for tvOS/macOS

* Fixed Logic on token expiry added more tests
Updated Test Dependencies
sam-w added a commit to LoungeBuddy/Auth0.swift that referenced this pull request Mar 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants