Firebase acces and id tokens - node.js

I'd like to know how to get both access and id tokens in Node.js SDK Firebase.
When I print user object after signUpWithEmailAndPassword, I see that accessToken is one the properties there, but then when i use method on user object called getIdToken, I get the same token I saw in users object. Why then it is not called getAccessToken???
What I want is return to the client object containing access, id, refresh tokens and expiration time.
P.S. I can't just say user.stsTokenManager.accessToken as it tells me that there is no already such property.

This is only an internal name. This "accessToken" is really the Firebase ID token. You should rely on the officially supported getIdToken to get that Firebase ID token. Firebase also recently added getIdTokenResult which provides the ID token and additional information like expiration time and other token related information without you having to parse it from the ID token. You can also get the refreshToken from the user via firebase.auth().currentUser.refreshToken.

const result = await getRedirectResult(auth);
if (result) {
const provider = new FacebookAuthProvider();
// This is the signed-in user
const user = result.user;
// This gives you a Access Token.
const credential = provider.credentialFromResult(auth, result);
const accessToken = credential.accessToken;
// this gives you the id token
const idToken = user.getIdToken();
}

Related

Edit User's Custom Claims from Firebase

I am using firebase to generate JWT tokens to authorize access to a hasura graphql server.
I want an end user to have a callable firebase function that they can call from the app so they can change the x-hasura-role in their claims without changing other parts of their claims. I am guessing the best way to do this is to export the old custom user claims and set a new role inputted by the user.
PseudoCode:
exports.changeUserType = functions.https.onCall( async (data, context) => {
var userType = data.usertype;
// get the old user claims somehow
// check if user should be able to change their userType via a graphql query
...
// edit the user claims
return admin.auth().setCustomUserClaims(userType, {
'https://hasura.io/jwt/claims': {
'x-hasura-role': userType,
'x-hasura-default-role': 'orgdriver',
'x-hasura-allowed-roles': ['orgauditor', 'orgdriver', 'orgmanager', 'orgadmin', 'orgdirector'],
'x-hasura-user-id': user.uid // <-- from the old claims so user can't edit
}
});
If there is a better way to do this, maybe by grabbing a user's id from the auth database by checking who ran the function please tell me. Thank you in advance.
When a Firebase Authenticated user hits a Firebase Function, their uid is passed in through context. I would ensure they are authenticated first:
if (context.auth == undefined) {
throw new functions.https.HttpsError(
'failed-precondition',
'The user must be authenticated.',
);
}
Then I would grab their uid:
const uuid = context?.auth?.uid as string;
Then you can get their user using the firebase-admin library's getAuth():
// get user
const user = await getAuth().getUser(uuid);
Now finally you can set your new custom claim property:
// set the hasura role
return await getAuth().setCustomUserClaims(uuid, {
...user.customClaims,
'x-hasura-role': userType,
});
Be sure to import:
import { getAuth } from 'firebase-admin/auth';
In this way you can safely know the user is authenticated and a uid exists, then you can simply grab the user and all their existing claims, then when you go to update destructure all existing claims values, and update the one value you want.
In this way get all the user's old claims, ensure they are authenticated, retain all old claim properties, and update the one thing you want to update.
I hope that helps out!

Token management for ClientSecretCredential usage in MSGraph API

I need to send mails from a background application(worker service/azure function) using MSGraph API with Application permission. This is how I have initialized the GraphServiceClient to send emails.
var credentials = new ClientSecretCredential(
"TenantID",
"ClientId",
"ClientSecret",
new TokenCredentialOptions { AuthorityHost = AzureAuthorityHosts.AzurePublicCloud });
GraphServiceClient graphServiceClient = new GraphServiceClient(credentials);
var subject = $"Subject from demo code";
var body = $"Body from demo code";
// Define a simple e-mail message.
var email = new Microsoft.Graph.Message
{
Subject = subject,
Body = new ItemBody
{
ContentType = BodyType.Html,
Content = body
},
ToRecipients = new List<Recipient>()
{
new Recipient { EmailAddress = new EmailAddress { Address = "EmailId" }}
}
};
// Send mail as the given user.
graphServiceClient
.Users["UserID"]
.SendMail(email, true)
.Request()
.PostAsync().Wait();
How long the graphServiceClient will have a valid token and how to regenerate a token when the token is expired.
What are the best practices for this usage
The default lifetime of an access token is variable. When issued, an access token's default lifetime is assigned a random value ranging between 60-90 minutes (75 minutes on average). Please refer this DOC for more information.
The refresh tokens can be used to acquire access tokens if they expire, Refresh tokens are long-lived, and can be used to retain access to resources for extended periods of time.
For getting the access tokens with client secret please refer this method of getting the access tokens.
Hope this will help.
As of May 2022, there is no option to generate a refresh token while using client_credentials options.
For client_credentials option
It's possible to generate a refresh token while using on behalf of user
For on behalf of a user

Endpoint to fetch Subreddits of a Reddit Account

I have completed the oauth flow for my third party app against a Reddit account and I've gotten the access token for the account.
Now my next issue is
How can I fetch the subreddits for an account using the access token
I can't seem to figure out the endpoint for that.
Does anyone know the endpoint for that?
Thank you
The Reddit OAuth Docs say that for the /subreddits/mine/(where) endpoint, the subreddits OAuth scope is necessary.
Once that scope is acquired for a user, you can use the following snippets of code to access the list of subscribed subreddits for the user:
View a users subreddits                                                                                
View in Fusebit
// Demonstrate using snooclient and Fusebit
const subscriptions = await redditClient.getSubscriptions().fetchAll();
// OR fetch the first page using a raw HTTP request
// - the User-Agent is necessary, don't forget it!
const access_token = redditClient.fusebit.credentials.access_token;
const httpSubs = await superagent.get(
'https://oauth.reddit.com/subreddits/mine/subscriber')
.set('Authorization', `Bearer ${access_token}`)
.set('User-Agent', 'Fusebit Integration');
const length = httpSubs.body.data.children.length;
ctx.body = {
usingSnoo: `User has ${subscriptions.length} subreddits`,
usingHttp: `The first page has ${length} subreddits`,
};
});

Refresh Token is not returned in #azure/msal-node using Authorization code flow?

In example project provided by Microsoft here which uses Authorization code flow the acquireTokenByCode method does not return refresh tokens.
From #azure/msal-node here refresh token is not mentioned.
Result returned from the authority's token endpoint.
uniqueId - oid or sub claim from ID token
tenantId - tid claim from ID token
scopes - Scopes that are validated for the respective token
account - An account object representation of the currently signed-in user
idToken - Id token received as part of the response
idTokenClaims - MSAL-relevant ID token claims
accessToken - Access token received as part of the response
fromCache - Boolean denoting whether token came from cache
expiresOn - Javascript Date object representing relative expiration of access token
extExpiresOn - Javascript Date object representing extended relative expiration of access token in case of server outage
state - Value passed in by user in request
familyId - Family ID identifier, usually only used for refresh tokens
please ensure your MSAL authorization code request includes the offline_access scope.
You could use MSAL.js to get token in this case, there is acquireTokenSilent method, it can perform silent renewal of tokens, which means you are no need to get the refresh token by yourself.
Popup
var request = {
scopes: ["Mail.Read"]
};
msalInstance.acquireTokenSilent(request).then(tokenResponse => {
// Do something with the tokenResponse
}).catch(async (error) => {
if (error instanceof InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenPopup(request);
}
}).catch(error => {
handleError(error);
});
Redirect
var request = {
scopes: ["Mail.Read"]
};
msalInstance.acquireTokenSilent(request).then(tokenResponse => {
// Do something with the tokenResponse
}).catch(error => {
if (error instanceof InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return myMSALObj.acquireTokenRedirect(request)
}
});
It's designed to not return the refresh token if you are using #azure/msal-node.
As they stated in the discussion, the refresh token is handled background, inside the library itself for better security, which I also disagree with.
However, if you insist to have the token, you can manually call the API to the AzureAD endpoint.

Are fcmTokens and ID Tokens the same and how to verify them with Node.js as a cloud function?

My app uses fcmTokens assigned to a user and stored in a Firestore document to keep track of app installations and logins. When a user logs out of the app I delete the fcmToken from the Firestore document and run InstanceID.instanceID().deleteID.
However when the user has bad internet 'InstanceID.instanceID().deleteID' is run again when the app starts the next time. The fcmToken in the Firestore document is not deleted in this case.
Theoretically I could also run a query in the app and search for this token in all of the Firestore user documents and delete it there but I rather would like to use cloud functions to check if the fcmTokens of a user are still valid. If not I want to delete them. I started writing the following cloud function but I am getting an error saying
Decoding Firebase ID token failed. Make sure you passed the entire string JWT which represents an ID token. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
I assume I am using the wrong function and fcmTokens are not the same as ID Tokens?
Is there a way to check for the validity of the fcmToken similar to how I check here for the (non existent) ID Token.
Or should I somehow use ID Tokens in general for managing device specific login? ( I'm using a Snapshot listener that listens for fcmToken changes and I am logging the user out when a specific fcmToken is deleted.)
Here is my cloud function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
var userA_UID = ""
exports.checkFcmToken = functions.firestore.document('registeredUsers/{userA_UID}').onUpdate(async (snapshot, context) => {
userA_UID = context.params.userA_UID;
const userInfo = await admin.firestore().collection('registeredUsers').doc(userA_UID).get();
const fcmTokens = userInfo.data()['fcmTokens'];
if (fcmTokens !== undefined) {
if (fcmTokens.length > 0) {
for (let fcmToken of fcmTokens) {
checkToken(fcmToken)
}
}
}
function checkToken(fcmToken) {
//will delete token from document array if invalid
admin.auth().verifyIdToken(fcmToken)
.then((decodedToken) => {
let uid = decodedToken.uid;
console.log(uid)
throw new Error('Error!')
}).catch((error) => {
console.log(error)
});
}
})
FCM tokens and ID tokens are quite different, and cannot be used interchangeably.
Firebase Authentication ID tokens identify a user. This means that if the same user is signed in on two different devices, they have ID tokens that identify the same user.
FCM tokens (also called Instance ID tokens) identify an application installation. If you have two tokens from two different devices, there is nothing that is shared between those tokens.
FCM tokens are opaque strings, and cannot be be verified without calling the FCM API.
When you send a message to an outdated token, the FCM API responds with a clear error message. The idiomatic way to keep your list of tokens clean is to handle this error message and remove the outdated token, as shown in this example from the Cloud Functions repo.

Resources