The session is stored in cookies. When I use this {session ? `${session.user.email}` : ''} it keeps saying undefined.
This is my JSON object:
{
"user": {
"token": {
"responseCode": 0,
"responseMessage": "success",
"data": {
"id": 6,
"userId": "SYS-9d502c43-9ef3-432d-ab93-be0b8236cdff",
"fullName": "yeshewas string",
"username": "yes#yes.com",
"userRole": "Super User",
"permissions": []
}
}
},
"expires": "2022-06-24T06:14:39.011Z"
}
Try to change your jwt callback like this:
callbacks: {
jwt: async ({ token, user }) => {
user && (token.user = user.user)
return token;
},
},
Related
I am using Firebase Authentication with Identify Platform and am trying to add custom claims when a user is created. I am looking at this example from Google's website here: Setting custom and session claims:
exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
if (context.credential &&
context.credential.providerId === 'saml.my-provider-id') {
return {
// Employee ID does not change so save in persistent claims (stored in
// Auth DB).
customClaims: {
eid: context.credential.claims.employeeid,
},
// Copy role and groups to token claims. These will not be persisted.
sessionClaims: {
role: context.credential.claims.role,
groups: context.credential.claims.groups,
}
}
}
});
The code is straight forward. I am trying to add custom claims for all new users but they are not getting set. I am not sure how else to to try. This is my actual code:
exports.beforeUserCreate = functions.auth.user().beforeCreate((user, context) => {
functions.logger.info('Attempting to set claims for new user', user);
functions.logger.info('Here is the context', context);
return {
customClaims: {
roles: ['user'],
},
sessionClaims: {
roles: ['user'],
},
};
});
I do see the logs in the Google console, so I know my function is being called. I also tested the claims without the array like roles: 'TestRole', but that didn't work either. The user object just does not have the custom claims.
If I manually set the claims they do show up as expected:
{
"roles": [
"admin",
"subscriber",
"superadmin"
],
"iss": "https://securetoken.google.com/...",
"aud": "xxx",
"auth_time": 1661813313,
"user_id": "xxxx",
"sub": "xxx",
"iat": 1661813313,
"exp": 1661816913,
"email": "xxx",
"email_verified": false,
"firebase": {
"identities": {
"email": [
"xx"
]
},
"sign_in_provider": "password"
}
}
This is what the user object looks like when I try to create the claims automatically:
{
"iss": "https://securetoken.google.com/...",
"aud": "xxx",
"auth_time": 1661813351,
"user_id": "xxx",
"sub": "xxx",
"iat": 1661813351,
"exp": 1661816951,
"email": "xxx",
"email_verified": false,
"firebase": {
"identities": {
"email": [
"xxx"
]
},
"sign_in_provider": "password"
}
}
Also, I tried setting both customClaims and sessionClaims independently. Neither show up on the user object, nor are the custom claims saved for the user.
One more update. I tried setting the display name in beforeCreate and that worked.
return {
customClaims: {
roles: 'pie',
},
displayName: 'pie',
};
// RESULT:
{
"name": "pie",
"iss": "https://securetoken.google.com/...",
"aud": "xxx",
"auth_time": 1661816987,
"user_id": "xxx",
"sub": "xxx",
"iat": 1661816987,
"exp": 1661820587,
"email": "xxx",
"email_verified": false,
"firebase": {
"identities": {
"email": [
"xxx"
]
},
"sign_in_provider": "password"
}
}
From Darwin in comments:
Hi #Gremash , there's an open github issue regarding that. See sessionClaims content not getting added to the decoded token. Also, there's a fix that has been recently merged regarding this issue.
Here is my code below, I'm trying to send a post request using Firebase Firestore Rest API. It works, but it generates a random ref id token that links it to the post data (fields) I sent, which makes it difficult to relate to.
I want to be able to link my post data with a custom id token.
final String customIdToken9 = 'customIdToken9'
String url = "https://firestore.googleapis.com/v1/projects/myAppId/databases/(default)/documents/customers/$customIdToken9"
const _body = {
"fields": {
"androidNotificationToken": {
"nullValue": null
},
"fullname": {
"stringValue": "Custom"
},
"uid": {
"stringValue": "$customIdToken9"
},
"admin": {
"stringValue": ""
},
"email": {
"stringValue": "customer1#gmail.com"
},
"photo": {
"stringValue": ""
},
"coverPhoto": {
"stringValue": ""
},
"bio": {
"stringValue": ""
},
"role": {
"stringValue": "user"
},
"mobile": {
"stringValue": "092222"
}
}
}
http.post(url, body = _body);
When I run this code above it returns
{
"error": {
"code": 400,
"message": "Document parent name \"projects/MyAppId/databases/(default)/documents/customers\" lacks \"/\" at index 59.",
"status": "INVALID_ARGUMENT"
}
}
If I remove the customTokenId9, it works, but it generates a random token Id, which I don't want
String url = "https://firestore.googleapis.com/v1/projects/myAppId/databases/(default)/documents/customers/"
http.post(url, body = _body);
If you want to specify your own document name, you need to add a query paramater called documentId to your URL request. Here's how it should look like on your code:
final String customIdToken9 = 'customIdToken9'
String url = "https://firestore.googleapis.com/v1/projects/myAppId/databases/(default)/documents/customers?documentId=$customIdToken9"
const _body = {
"fields": {
"androidNotificationToken": {
"nullValue": null
},
"fullname": {
"stringValue": "Custom"
},
"uid": {
"stringValue": "$customIdToken9"
},
...
}
}
The client-assigned document ID to use for creating a document. It is optional. If not specified, a random ID will automatically be assigned by the service.
Full description is stated in this link and you'll see it in the Query Parameters section. You can also try to test out the API and expand it to see its curl equivalent.
Additionally, you can look up to this documentation to understand how a query parameter works. It applies on any REST API's offered by Firebase.
Why is your variable final String customIdToken but you are using $customIdToken9 it should be $customIdToken.
My purpose is to secure my backend, which is deployed as GCP App Engine, with an API-Gateway, provided by Google, to only let Firebase authenticated users have access to it.
I set everything up, as stated in the documents:
API-Gateway config:
{
"swagger": "2.0",
"info": {
"title": "PROJECT_NAME",
"version": "1.0"
},
"securityDefinitions": {
"firebase": {
"authorizationUrl": "",
"flow": "implicit",
"type": "oauth2",
"x-google-issuer": "https://securetoken.google.com/PROJECT_ID",
"x-google-jwks_uri": "https://www.googleapis.com/service_accounts/v1/metadata/x509/securetoken#system.gserviceaccount.com",
"x-google-audiences": "PROJECT_ID"
}
},
"security": [
{
"firebase": [ ]
}
],
"paths": {
"/hello-spring": {
"get": {
"produces": [
"application/json"
],
"operationId": "helloSpring",
"parameters": [],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
}
},
"x-google-backend": {
"address": "https://URI-TO-APP-ENGINE-SERVICE",
"path_translation": "APPEND_PATH_TO_ADDRESS"
"jwt_audience": "API_CREDENTIALS_FROM_IAP_APP_ENGINE_APP"
}
}
}
},
"definitions": {
"User": {
"properties": {
"creationTimestamp": {
"format": "date-time",
"type": "string"
},
"id": {
"format": "int32",
"type": "integer"
},
"name": {
"type": "string"
},
"updateTimestamp": {
"format": "date-time",
"type": "string"
},
"uuid": {
"type": "string"
}
},
"type": "object"
}
},
"x-components": {}
}
When I send a request - without a jwt token - to this api-gateways url, it correctly responds with
{
"message": "Jwt is missing",
"code": 401
}
Firebase jwt
"Default" firebase backend is used, no custom tokens are created. Firebase is used to create the JWT token. From the client following code snippet is used to receive a valid JWT:
final FirebaseAuth _auth = FirebaseAuth.instance;
....
final GoogleSignInAccount googleSignInAccount = await googleSignIn.signIn();
final GoogleSignInAuthentication googleSignInAuthentication = await googleSignInAccount.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleSignInAuthentication.accessToken,
idToken: googleSignInAuthentication.idToken,
);
final UserCredential authResult = await _auth.signInWithCredential(credential);
final String token = await _auth.currentUser.getIdToken();
But as soon as I add the JWT (final String token in code above), which is created by a firebase client app and add it to the request header with
Authorization: Bearer TOKEN_CODE
gateway responds with
{
"code": 401,
"message": "Jwt verification fails"
}
The most detailed LOG I can see in GCP is:
response_code_detail: jwt_authn_access_denied{Jwt_verification_fails}
I also created a service account linked to this API-Gateway with following roles:
roles/appengine.appViewer
roles/iam.serviceAccountUser
roles/run.invoker
roles/apigateway.viewer
But still, the same error: "401 - Jwt verification fails". What am I missing,how can I see more details about this issue in GCP and what could be a solution?
I am working with Meteor User accounts to create users. I have implemented two ways of creating users.
By using accounts-password to create (default one ).
OAuth Services (accounts-google and accounts-facebook)
A user account generated with accounts-password have the document shown below
{
"_id": "DQnDpEag2kPevSdJY",
"createdAt": "2015-12-10T22:34:17.610Z",
"services": {
"password": {
"bcrypt": "XXX"
},
"resume": {
"loginTokens": [
{
"when": "2015-12-10T22:34:17.615Z",
"hashedToken": "XXX"
}
]
}
},
-----
----
}
Where as a user account generated with accounts-google or account-facebook have the document shown below.
{
"_id": "Ap85ac4r6Xe3paeAh",
"createdAt": "2015-12-10T22:29:46.854Z",
"services": {
"facebook": {
"accessToken": "XXX",
"expiresAt": 1454970581716,
"id": "XXX",
"email": "myname#gmail.com",
"name": "Ada Lovelace",
"first_name": "Ada",
"last_name": "Lovelace",
"link": "https://www.facebook.com/app_scoped_user_id/XXX/",
"gender": "female",
"locale": "en_US",
"age_range": {
"min": 21
}
},
---
---
---
Now the real issue is, Although the email address used is same for both accounts-password and accounts-google (in my case email is myname#gmail.com), two different user accounts are being created.
I am looking for solution Something like below. (Note: Services has both "Password" and "Facebook" sections under single account)
{
"_id": "DQnDpEag2kPevSdJY",
"createdAt": "2015-12-10T22:34:17.610Z",
"services": {
"password": {
"bcrypt": "XXX"
},
"facebook": {
"accessToken": "XXX",
"expiresAt": 1454970581716,
"id": "XXX",
"email": "myname#gmail.com",
"name": "Ada Lovelace",
"first_name": "Ada",
"last_name": "Lovelace",
"link": "https://www.facebook.com/app_scoped_user_id/XXX/",
"gender": "female",
"locale": "en_US",
"age_range": {
"min": 21
}
},
},
-----
----
}
Is there a way where only one account is being generated in both cases, means if a user is already existed and the same is trying with OAuth service, first account should be used to accommodate the service ?
link-accounts package is the recommended way to allow users to add additional services to their account.
You can use Accounts.setPassword on the server in order to generate a proper bcrypt hash for the accounts:
Accounts.setPassword('Ap85ac4r6Xe3paeAh', 'the-new-password')
which will result in
{
"_id": "Ap85ac4r6Xe3paeAh",
"createdAt": "2015-12-10T22:29:46.854Z",
"services": {
"password": {
"bcrypt": "$2b$10$nzHCivxVqxbuFBBPWewPPu.r5x7OR5gJB8PIklU4OoU.WK0MT8jt2"
},
"facebook": {
"accessToken": "XXX",
"expiresAt": 1454970581716,
"id": "XXX",
"email": "myname#gmail.com",
"name": "Ada Lovelace",
"first_name": "Ada",
"last_name": "Lovelace",
"link": "https://www.facebook.com/app_scoped_user_id/XXX/",
"gender": "female",
"locale": "en_US",
"age_range": {
"min": 21
}
}
}
}
I have solved the above issue with a hack.
In imports/startup/server/accounts.js I have added the below validation logic which always validates the newly created account.
The idea is, this process checks if user is already existed in database. If the user exists, further checks if its created from accounts-password or accounts-google/facebook .
Based on the existing type modify the existing fields with new fields and throw an error with a fancy message (This actually prevents the new account to be created).
Accounts.validateNewUser(function (user) {
// first check what is the newly creating service
var service =
user.services.google || user.services.facebook || user.services.password;
if (!service) return true;
var existingUser = null;
// due to some issues both `Meteor.users.findOne(email)` as well `Account.findUserByEmail(email)` methods have been used to find the existing user status
if (user.services.password) {
var email = user.emails[0].address;
existingUser = Meteor.users.findOne({
$or: [
{ "registered_emails[0].address": email },
{ "services.google.email": email },
{ "services.facebook.email": email },
],
});
} else {
var email = service.email;
//console.log(" retrieved email ", email);
existingUser = Accounts.findUserByEmail(email);
}
//console.log(" existingUser : ", existingUser);
if (!existingUser) return true;
if (user.services.google) {
Meteor.users.update(
{ _id: existingUser._id },
{
$set: {
profile: user.profile,
"services.google": user.services.google,
},
}
);
} else if (user.services.facebook) {
Meteor.users.update(
{ _id: existingUser._id },
{
$set: {
profile: user.profile,
"services.facebook": user.services.facebook,
},
}
);
} else {
Meteor.users.update(
{ _id: existingUser._id },
{
$set: {
profile: user.profile,
"services.password": user.services.password,
"services.email": user.services.email,
emails: user.emails,
},
}
);
}
throw new Meteor.Error(
205,
"Merged with your existing Social Login accounts now. Try refresh the page and sign in again. That should work !!"
);
});
I'm using braintree for payment and I have done this.
gateway.customer.create({
firstName: "Sachin",
lastName: "Shah",
company: "Qwerty",
email: "Qwerty#example.com",
phone: "114.555.1234",
fax: "614.555.1234",
website: "www.example.com",
}, function (err, result) {
if (err) {
res.send({code:0, status:'Error', message:err});
}else{
res.send({code:1, status:'Success', data: result});
}
});
I followed it's official doc and they show that when req is success I'll get token but I'm gtting result.customer.paymentMethods[]
Response
{
"code": 1,
"status": "Success",
"data": {
"customer": {
"id": "569549779",
"merchantId": "XXXXXXXXXXXXXXXXX",
"firstName": "Sachin",
"lastName": "Shah",
"company": "Qwerty",
"email": "Qwerty#example.com",
"phone": "114.555.1234",
"fax": "614.555.1234",
"website": "www.example.com",
"createdAt": "2019-10-10T05:13:42Z",
"updatedAt": "2019-10-10T05:13:42Z",
"customFields": "",
"globalId": "XXXXXXXXXXXXXXXXX",
"creditCards": [],
"addresses": [],
"paymentMethods": []
},
"success": true
}
}
Expected Output
I need to get paymentMethodToken for further API calls.
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
You're currently creating a customer without a payment method. You'll need to create the customer with a payment method to retrieve a paymentMethodToken.