I'm trying to use firebase auth for hasura.
I've set HASURA_GRAPHQL_ADMIN_SECRET and also HASURA_GRAPHQL_JWT_SECRET using the generator and referencing my project id.
I then have this cloud function
`const functions = require('firebase-functions');
const admin = require('firebase-admin');
exports.addDefaultUserRole = functions.auth.user().onCreate((user) => {
let uid = user.uid;
//add custom claims
return admin.auth().setCustomUserClaims(uid,{
'https://hasura.io/jwt/claims': {
'x-hasura-default-role': 'user',
'x-hasura-allowed-roles': ['user'],
'x-hasura-user-id': user.uid
}
})
.then(() => {
return admin.auth().getUser(uid);
})
.then(userRecord => {
console.log(uid);
console.log(userRecord);
return null;
});
});
admin.initializeApp(functions.config().firebase);
`
..this successfully prints out in the firebase console the custom claims
passwordSalt: undefined,
customClaims:
{ 'https://hasura.io/jwt/claims':
{ 'x-hasura-default-role': 'user',
'x-hasura-allowed-roles': [Array],
'x-hasura-user-id': '7hlqtPrQviaAFPZBKqGdk0L6R1J2' } },
tokensValidAfterTime: 'Mon, 16 Mar 2020 12:03:13 GMT' }
But if I then take the token generated and set it with: Authorization: Bearer $token either in Hasura API explorer or in my client code I just get the following error
{
"errors": [
{
"extensions": {
"path": "$",
"code": "jwt-invalid-claims"
},
"message": "claims key: 'https://hasura.io/jwt/claims' not found"
}
]
}
I'm not sure what else I can debug to try and get to the route cause of this
@andrewpmoore - Can you check content of the $token. You can use the JWT inspector on the Hasura console (it's available on the headers pane where you enter the Authorization token) or you can also use https://jwt.io/#debugger-io to verify the contents of the token. It should give an idea of how the custom claims have been generated.
Thanks @praveenweb that's a handy feature I didn't know about :)
I see
claims key: 'https://hasura.io/jwt/claims' not found
Header:
{
"alg": "RS256",
"kid": "82e6b1c921fa86770f3d50c12c15d6eaca8f0d35",
"typ": "JWT"
}
Data:
{
"iss": "https://securetoken.google.com/test_project_hasura",
"aud": "test_project_hasura",
"auth_time": 1584364173,
"user_id": "yzNiCVEsPIVnh5BsgPiHQiAhBgj2",
"sub": "yzNiCVEsPIVnh5BsgPiHQiAhBgj2",
"iat": 1584364173,
"exp": 1584367773,
"email": "[email protected]",
"email_verified": false,
"firebase": {
"identities": {
"email": [
"[email protected]"
]
},
"sign_in_provider": "password"
}
}
so no sign of the claims, even though debugging on the firebase console shows this:
uid: 'yzNiCVEsPIVnh5BsgPiHQiAhBgj2',
email: '[email protected]',
emailVerified: false,
displayName: undefined,
photoURL: undefined,
phoneNumber: undefined,
disabled: false,
metadata:
UserMetadata {
creationTime: 'Mon, 16 Mar 2020 13:09:33 GMT',
lastSignInTime: 'Mon, 16 Mar 2020 13:09:33 GMT' },
providerData:
[ UserInfo {
uid: '[email protected]',
displayName: undefined,
email: '[email protected]',
photoURL: undefined,
providerId: 'password',
phoneNumber: undefined } ],
passwordHash: 'UkVEQUNURUQ=',
passwordSalt: undefined,
customClaims:
{ 'https://hasura.io/jwt/claims':
{ 'x-hasura-default-role': 'user',
'x-hasura-allowed-roles': [Array],
'x-hasura-user-id': 'yzNiCVEsPIVnh5BsgPiHQiAhBgj2' } },
tokensValidAfterTime: 'Mon, 16 Mar 2020 13:09:33 GMT' }
Is there something else I'm missing that could be causing this.
It seems the issue was that when I tried to get the token I wasn't waiting for it to update the claims on firebase so it still had the old token. Thanks
@andrewpmoore - Can you check content of the
$token. You can use the JWT inspector on the Hasura console (it's available on the headers pane where you enter the Authorization token) or you can also use https://jwt.io/#debugger-io to verify the contents of the token. It should give an idea of how the custom claims have been generated.
Where can I find the JWT inspector?
It seems the issue was that when I tried to get the token I wasn't waiting for it to update the claims on firebase so it still had the old token. Thanks
Can you please expand a little bit on this? I've got the same problem...
So into hasura grapiql tab.
In the headers if you set authorisation and bearer
So into hasura grapiql tab.
In the headers if you set authorisation and bearer at the side of where you paste it there should be a little inspector icon which when you click you'll see the data in your token and whether it's valid
I found that already thanks. But I meant the other part where you were saying "It seems the issue was that when I tried to get the token I wasn't waiting for it to update the claims on firebase so it still had the old token. Thanks"
Ok so I've basically got this in the cloud function
`exports.processSignUp = functions.auth.user().onCreate(async user => {
let customClaims;
return admin.auth().setCustomUserClaims(user.uid, {
'https://hasura.io/jwt/claims': {
'x-hasura-default-role': 'user',
'x-hasura-allowed-roles': ['user'],
'x-hasura-user-id': user.uid
}
})
.then(async () => {
await firestore.collection('users').doc(user.uid).set({
createdAt: admin.firestore.FieldValue.serverTimestamp()
});
})
.catch(error => {
console.log(error);
});
});
`
So once the user claims are set I then write a vale into firestore (just the userid and the time).
Then on the client side I'm subscribing to firestore to see when that data exists
`
DocumentReference userDocRef = Firestore.instance.collection('users').document(currentUser.uid);
Stream
DocumentSnapshot data = await docs.firstWhere((DocumentSnapshot snapshot) => snapshot?.data !=null && snapshot.data.containsKey('createdAt')); //if we get to here we've got custom claims set, don't need the actual return
print('data ${data.toString()}');
IdTokenResult idTokenResult = await (currentUser.getIdToken(refresh: true));
`
So before actually trying to set the bearer in hasura I await for this to finish and then use the idTokenResult which will have the custom claims added to it.
If you try and use the token before that it won't have been registered with the custom claims that were set on firebase as the "create user" returns before they've been set
Is there a way to apply claims before an account is created? In my app I don't use firestore and I'd rather not have a dependency on it just for this, but I can't think of another way to implement the claims at account creation.
I suppose you could query firebase auth once every two seconds until the claims appear....
Hey folks, you could also use our upcoming custom JWT mapping feature which can avoid the use of adding any custom claims to your firebase token. A blogpost on this (using the preview build) is here: https://cantaspinar.com/easier-authentication-with-hasura-jwt-claims-customization-firebase-auth/
Thanks @tirumaraiselvan that's way easier and removes another dependency on fire base cloud functions, further simplifying the stack. I'll try this out now.
Most helpful comment
Ok so I've basically got this in the cloud function
`exports.processSignUp = functions.auth.user().onCreate(async user => {
let customClaims;
return admin.auth().setCustomUserClaims(user.uid, {
'https://hasura.io/jwt/claims': {
'x-hasura-default-role': 'user',
'x-hasura-allowed-roles': ['user'],
'x-hasura-user-id': user.uid
}
})
.then(async () => {
await firestore.collection('users').doc(user.uid).set({
createdAt: admin.firestore.FieldValue.serverTimestamp()
});
})
.catch(error => {
console.log(error);
});
});
`
So once the user claims are set I then write a vale into firestore (just the userid and the time).
Then on the client side I'm subscribing to firestore to see when that data exists
` docs = userDocRef.snapshots(includeMetadataChanges: false);
DocumentReference userDocRef = Firestore.instance.collection('users').document(currentUser.uid);
Stream
`
So before actually trying to set the bearer in hasura I await for this to finish and then use the idTokenResult which will have the custom claims added to it.
If you try and use the token before that it won't have been registered with the custom claims that were set on firebase as the "create user" returns before they've been set