How to verify Authress JWT access tokens
For information about OAuth, check out the OAuth FAQ.
Backgroundโ
Authress generates unique EdDSA JWT signed access tokens. These tokens are signed using an asymmetric key algorithm. Your users will have tokens generated for them as part of the login process or you will generate JWTs to ensure strong authentication between your services.
To verify the incoming JWTs, the signature of the JWT needs to be validated using the Authress public keys for your account.
Once your account has a custom domain configured, the public keys for your account are located at:
https://auth.yourdomain.com/.well-known/openid-configuration/jwks
Authress User IDs and a JWT Access token exampleโ
The generated access tokens by Authress are OpenID compliant JWTs. That means they look like this:
eyJhbGciOiJFZERTQSIsImtpZCI6InB2ZTQ3OGlHU3g4VzJnc3p6UVlta1QiLCJ0eXAiOiJhdCtqd3QifQ.eyJpc3MiOiJodHRwczovL2xvZ2luLmF1dGhyZXNzLmlvIiwic3ViIjoiZ29vZ2xlLW9hdXRoMnwxMDkzNTcxMTkwNDI0NDc3MDAwNjIiLCJpYXQiOjE2Nzg4MjU5MzEsImV4cCI6MTY3ODkxMjMzMSwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCIsImF6cCI6Imdvb2dsZSIsImNsaWVudF9pZCI6IkFVVEhSRVNTIiwiYXVkIjpbImh0dHBzOi8vYXBpLmF1dGhyZXNzLmlvIl19.<sig>
And when decoded are an object that contains user information including their userId
.
{
"iss": "https://auth.yourdomain.com",
"sub": "authress_user_id_001",
"iat": 1678825931,
"exp": 1678912331,
"scope": "openid profile email",
"azp": "con_source_user_connection",
"client_id": "app_user_application",
"aud": [
"user_company_tenant_id"
]
}
You can get a user's userId
by decoding the JWT. However to ensure that the token is valid, instead we suggest using the verifyToken
function or the TokenVerifier
included in each of the Authress SDKs. Here the user ID is authress_user_id_001
. This is the same user ID that should be passed to each of the Authress API calls.
In the case you are using JWTs from another authentication system other than Authress, you can decode them in a similar way, and pass the decoded sub
to Authress. Authress can accept JWTs from anywhere, the Authress APIs are not restricted to only JWTs generated by Authress.
Verificationโ
Getting the public key is only the first step. Once you have the public keys fetched from the URLs above, you can verify incoming JWTs by the following process.
- Decode the JWT - Every JWT is a base64 encoded string. This string can be decoded to a JSON object.
- Pull out the KeyId (
kid
) and the Issuer (iss
) url - The issuer is Authress, and when using a custom domain, it is this domain. Authress stores multiple public keys for you, and can be identified using thekid
property. - Use the issuer url to fetch the public key from the
jwks
endpoint - Your apis are vulnerable without verifying the incoming token. Anyone can make a JWT, using the issuer's public keys is the only way to ensure your apis are secure. - Verify the signature of the JWT using the public key - In the case the signature is not valid, reject the request.
Once you have verified the signature, you can identify the user by using the Subject claim (sub
) of the token.
To simplify the steps above the most of the Authress SDKs contain a TokenVerifier
class or method to enable easy access to verifying Authress tokens. The use of the SDK is not required, however due to the complexity of doing this they are recommended. A list of SDKs is available in the Authress API Portal.
Key lifetimeโ
The Authress public key can and should be cached to avoid unnecessary lookups. However, it is important to not cache indefinitely. In rare events the key will rotate. It may be rotated through a security generated event in Authress or through the management portal. Given this possibly, it is important to be prepared to handle key rotation and expiry.
Additionally, it is the case that your Authress account can and will have multiple active keys at the same time. While not all of these keys will be relevant to signing access tokens, handling for multiple keys is required.
Example Verifiersโ
AWS Lambda Authorizerโ
import { AuthressClient } from '@authress/sdk';
import cookieManager from 'cookie';
import Api from 'openapi-factory';
let api = new Api({});
module.exports = api;
api.setAuthorizer(async request => {
const authorization = Object.keys(request.headers).find(key => key.match(/^Authorization$/i));
const token = request.headers[authorization] ? request.headers[authorization].split(' ')[1] : null;
if (!token) { throw Error.create('Unauthorized'); }
try {
const cookies = cookieManager.parse(request.headers.cookie || '');
const userToken = cookies.authorization || request.headers.Authorization.split(' ')[1];
// The url should be your custom domain => https://authress.io/app/#/setup?focus=domain
const authressCustomDomain = 'https://auth.yourdomain.com';
const authressClient = new AuthressClient({ authressApiUrl: authressCustomDomain });
const userIdentity = await authressClient.verifyToken(userToken);
const policy = {
principalId: userIdentity.sub,
policyDocument: {
Version: '2012-10-17',
Statement: [
{
Effect: 'Allow',
Action: ['execute-api:Invoke'],
Resource: ['arn:aws:execute-api:*:*:*']
}
]
},
context: {
jwt: userToken,
principalId: userIdentity.sub
}
};
return policy;
} catch (error) {
throw Error('Unauthorized');
}
});