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

Add ID token validation #285

Merged
merged 6 commits into from
Oct 8, 2018
Merged

Add ID token validation #285

merged 6 commits into from
Oct 8, 2018

Conversation

joshcanhelp
Copy link
Contributor

This SDK already had a JWTVerifier class to verify ID tokens but that class was not being used in the default login route. This PR adds this validation to Auth0\SDK\Auth0::setIdToken() before the incoming ID token is stored. Underlying validation library is Firebase JWT.

What is validated

  • Issuer
  • Audience
  • Signature
  • NBF
  • IAT
  • Expiration

Potential issues

NBF and IAT validation

Both of these values check a timecode in the token payload against the time set on the server doing the validation. If your server time is different from the issuing server, this could cause the validation to fail. If you're getting an error message stating Cannot handle token prior to ..., then you can try setting a leeway in the Firebase JWT library, like this:

// Set this before the Auth0 class is initialized.
// Value is in seconds and should be no greater than a few minutes.

\Firebase\JWT\JWT::$leeway = 30;

// ... 
$auth0 = new Auth0( [ 
    // ...
] );

Client Secret encoding

Most tenants will not have a base64-encoded Client Secret but older tenants might. It will display on your Application settings page whether this is the case or not:

screenshot 2018-09-05 16 49 55

By default, the JWT verifier uses the Client Secret as-is with no decoding. If your Client Secret is encoded, add a new value to your Auth0 initialization array like this:

$auth0 = new Auth0( [ 
    // ...
    'secret_base64_encoded' => true,
    // ...
] );

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.

please add the missing tests

src/Auth0.php Outdated
'client_secret' => $this->clientSecret,
'secret_base64_encoded' => $this->clientSecretEncoded,
'authorized_iss' => [ $issuer ],
'supported_algs' => [ 'HS256', 'RS256' ],
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be a setting / parameter?

src/Auth0.php Outdated
'secret_base64_encoded' => $this->clientSecretEncoded,
'authorized_iss' => [ $issuer ],
'supported_algs' => [ 'HS256', 'RS256' ],
'valid_audiences' => [
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO this should be a param too. What if the user requests a token for a custom API ? audience will appear here

src/Auth0.php Outdated
$issuer = 'https://'.$this->domain.'/';
$jwtVerifier = new JWTVerifier([
'guzzle_options' => $this->guzzleOptions,
'client_secret' => $this->clientSecret,
Copy link
Contributor

Choose a reason for hiding this comment

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

what's this and the param below user for if the alg happens to be RS?

src/Auth0.php Outdated
} catch (\Exception $e) {
$message = 'ID token could not be validated.';
if ($e->getMessage()) {
$message .= ' '.$e->getMessage();
Copy link
Contributor

Choose a reason for hiding this comment

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

why do you need to prepend ' '? Can't you do that on the "if" with something like isEmpty($e->getMessage())?

src/Auth0.php Outdated
@@ -556,9 +565,35 @@ public function setAccessToken($accessToken)
* @param string $idToken - ID token returned from the code exchange.
*
* @return \Auth0\SDK\Auth0
*
* @throws CoreException If ID token did not validate.
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd add here or in the readme (docs, wherever) all the special cases you mentioned on the PR's description. e.g. the leeway when time verification fails

@joshcanhelp joshcanhelp force-pushed the add-id-token-validation branch 2 times, most recently from aec4347 to 281bd0b Compare September 28, 2018 17:28
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.

💣 💥 🙉


/**
* @param array $scopes Array of scopes to include.
* @param integer $lifetime Lifetime of the token.
Copy link
Contributor

Choose a reason for hiding this comment

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

minor but "in seconds"

*/
protected function bstr2bin($input)
public function __construct($client_id, $client_secret)
Copy link
Contributor

Choose a reason for hiding this comment

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

since this class is called "TokenGenerator" what about renaming this props to audience and secret, so they refer more to a generic token rather than auth0

* Class TokenGenerator.
* Generates HS256 ID tokens.
*
* @package Auth0\SDK\API\Helpers
Copy link
Contributor

Choose a reason for hiding this comment

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

I imagine this class is only used on tests, right? If so, wouldn't the correct namespace be under a test folder?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's how it's used in the SDK, yes, but it's already in a public namespace so removing would be breaking. Making a note of this for the major.

src/Auth0.php Outdated
@@ -71,6 +72,13 @@ class Auth0
*/
protected $clientSecret;

/**
* Is the Auth0 Client Secret encoded?
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd not make this a question and add where I can know this information. e.g. "Check in your Application's settings under the Client Secret field."

/**
* Algorithm to use for ID token validation.
*
* @var string
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess there are a set of allowed values?

*
* @codeCoverageIgnore
*/
private function supportsAlg($alg)
Copy link
Contributor

Choose a reason for hiding this comment

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

I know you're using this in 2 or 3 places so I get why you moved this to a method. But this sounds more like a "array contains" method. (which you're using in many other places such as when comparing audiences). Consider refactoring this 🆗

*/
private function decodeTokenSegment($segment)
{
return JWT::jsonDecode(JWT::urlsafeB64Decode($segment));
Copy link
Contributor

Choose a reason for hiding this comment

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

why not make the "base64 decode part" call the method you defined above --> decodeB64 ?

@@ -1,12 +1,14 @@
<?php
namespace Auth0\Tests\API;

use Auth0\Tests\Traits\ErrorHelpers;
use Auth0\SDK\API\Helpers\TokenGenerator;
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I mentioned this in a comment above, but this token generator seems to be only used on this test. Please move it to the /Test/ folder so users don't use it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

See above

*
* @return void
*/
public function testThatMissingSecretThrowsException()
Copy link
Contributor

Choose a reason for hiding this comment

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

in general, use the method name to describe the test as most as possible. e.g. testThatMissingSecretWhenUsingHS256ThrowsException

'client_secret' => $client_secret,
'secret_base64_encoded' => false
]
// 2. Test that a non-encoded client secret can be used.
Copy link
Contributor

Choose a reason for hiding this comment

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

You might want to split this test into 2 separate ones. When a test fails is easier to read that by reading the test/method name rather than having to look at the line of code that caused this to fail.

*
* @var JWKFetcher|null
*/
protected $jwks_fetcher = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

this is protected. Changing this name will make subclasses break AFAIK.

@joshcanhelp joshcanhelp merged commit bdbe01e into master Oct 8, 2018
@joshcanhelp joshcanhelp deleted the add-id-token-validation branch October 8, 2018 20:57
@github-actions
Copy link
Contributor

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants