- Published on
Auth0 Unauthorized JWT Malformed Error
- Authors
- Name
- Yair Mark
- @yairmark
Auth0 is an awesome tool. But one issue we ran into when using it was that the configuration you need to use from the UI and server is not clear in the tutorials. They speak about ${authConfig.domain}
which is not 100% clear.
In my case, I ran into the following error when trying to hit an authorized endpoint on the express side: UnauthorizedError: jwt malformed
.
My (wrong) configuration on the UI looked as below:
import auth0 from 'auth0-js'
//...
this.auth0 = new auth0.WebAuth({
domain: 'my-test-app-foo-bar.au.auth0.com',
audience: 'https://my-test-app-foo-bar.au.auth0.com/userinfo',
clientID: 'AAAAAAAAAAAAAAAAAAAAAAAAlNbfTQGT',
redirectUri: 'http://127.0.0.1:3000/callback',
responseType: 'code token id_token',
scope: 'openid profile email',
})
The backend uses express with the Auto0 middleware. The (wrong) configuration for that looked like this:
var jwt = require('express-jwt')
var jwksRsa = require('jwks-rsa')
var checkJwt = jwt({
// Dynamically provide a signing key
// based on the kid in the header and
// the signing keys provided by the JWKS endpoint.
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: 'http://127.0.0.1:3002/.well-known/jwks.json',
}),
// Validate the audience and the issuer.
audience: 'https://my-test-app-foo-bar.au.auth0.com/api/v2/',
issuer: 'http://127.0.0.1:3002/',
algorithms: ['RS256'],
})
module.exports = checkJwt
After a bit of Googling my colleague and I came across this post.
Based on this my wrong configuration for the UI was changed to:
import auth0 from 'auth0-js'
//...
this.auth0 = new auth0.WebAuth({
domain: 'my-test-app-foo-bar.au.auth0.com',
audience: 'https://my-test-app-foo-bar.au.auth0.com/api/v2/', //the end bit was changed
issuer: 'https://my-test-app-foo-bar.au.auth0.com', //added
clientID: 'AAAAAAAAAAAAAAAAAAAAAAAAlNbfTQGT',
redirectUri: 'http://127.0.0.1:3000/callback',
responseType: 'code token id_token',
scope: 'openid profile email',
})
The server portion was changed to:
var jwt = require('express-jwt')
var jwksRsa = require('jwks-rsa')
var checkJwt = jwt({
// Dynamically provide a signing key
// based on the kid in the header and
// the signing keys provided by the JWKS endpoint.
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: 'https://my-test-app-foo-bar.au.auth0.com/.well-known/jwks.json',
}),
// Validate the audience and the issuer.
audience: 'https://my-test-app-foo-bar.au.auth0.com/api/v2/',
algorithms: ['RS256'],
})
module.exports = checkJwt
When you login to Auth0 and have everything configured properly you should see a response similar to the below (this is an example where Google was used to login - some data has been changed for privacy but the length of the strings is accurate):
{
"accessToken": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.cccccccccccccccccccccccccccccccccccccc-ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd-_eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"idToken": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc-ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd-eeee-fffffffffffffffffffffffffffffffffffffff-gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg-hhhh-iiiiiiiiiiiii",
"idTokenPayload": {
"given_name": "John",
"family_name": "Smith",
"nickname": "johnsmith",
"name": "John Smith",
"picture": "https://lh5.googleusercontent.com/-k-AAAAAAAAA/AAAAAAAAAAI/AAAAAAAAAAA/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB/photo.jpg",
"locale": "en-GB",
"updated_at": "2019-09-09T14:56:48.889Z",
"email": "[email protected]",
"email_verified": true,
"iss": "https://my-test-app-foo-bar.au.auth0.com/",
"sub": "google-oauth2|777777777777777777777",
"aud": "TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT",
"iat": 1568041008,
"exp": 1568077008,
"at_hash": "AAAAAAAAA_BBBBBBBBBBBB",
"c_hash": "CCCCCCCCCCCCCCCCCCCCCC",
"nonce": "AAAAA-BB.CCCCCCCCCCCCCCCCCCCCCCC"
},
"appState": null,
"refreshToken": null,
"state": "AAAAAA.BBCC.DDDDDDDDDDDDDDDDDDD-",
"expiresIn": 7200,
"tokenType": "Bearer",
"scope": null
}
Any requests that get sent to the server that needs to be authenticated should include a header with accessToken
from above as the bearer as in the below example that uses Axios:
var instance = axios.create({
baseURL: 'http://127.0.0.1:3002/',
timeout: 1000,
headers: { Authorization: `Bearer ${accessToken}` },
})
When using this with the Auth0 middleware above and using the checkJwt
method, if all is good the express req object is enriched with a user
object which looks like below:
{ iss: 'https://my-test-app-foo-bar.au.auth0.com/',
sub: 'google-oauth2|777777777777777777777',
aud:
[ 'https://my-test-app-foo-bar.au.auth0.com/api/v2/',
'https://my-test-app-foo-bar.au.auth0.com/userinfo' ],
iat: 1568041885,
exp: 1568049085,
azp: 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
scope: 'openid profile email' }
In the above a few things to note:
.au.
is because the region we chose for Auth0 is Au -> Australia. Yours will differ/be the same depending on the region you chose.- A telltale sign that your application token is wrong is if it is too short - ours was about 30 characters instead of a much much bigger string (as can be seen from the example response further above).
- You can use
sub
to uniquely identify a user in your database. The user can revoke/invalidate this at any time should they wish.