I am using firebase google auth to manage users. Further, I have to integrate the users calendar using the calendar API, now firebase auth provides ID Token, ID Refresh token & oAuth token but not oAuth refresh token, due to which I will have to obtain the oAuth token & oAuth refresh token using gAPI, now I got that, but is there a way to use those tokens to create a firebase auth user? I know there is method to create sign in with signInWithCredential but this takes ID Token and not oAauth token.
UPDATE
Sign In code:
const { client_secret, client_id, redirect_uris } = credentials.web;
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: SCOPES,
});
// Get code from the URL
oAuth2Client.getToken(code, (err, token) => {
oAuth2Client.setCredentials(token);
});
After gAPI signin, I am getting following details:
{
"access_token": "xxx",
"refresh_token": "xxx",
"scope": "https://www.googleapis.com/auth/calendar",
"token_type": "Bearer",
"expiry_date": 1613023105889
}
To access the ID Token, you have to add additional scope to the GAPI OAuth request. Along with your calendar scope, add "openid". Then in the token response, you should have access to id_token.
Also, you can skip the above step and exchange the OAuth token from google with firebase to get firebase ID Token.
Official docs: https://firebase.google.com/docs/reference/rest/auth/#section-sign-in-with-oauth-credential
Related
I use firebase at client and use firebase-admin at server.
At client i use signInWithPopUp to sign in to my web app with google account, then receive accessToken, expirationTime, refreshToken at client.
stsTokenManager:{
accessToken: "eyJhbGciOiJzGcaUzI1JKKIsIzxcXzStpZC... ,
expirationTime: 1648809531166 ,
refreshToken: "AIwxAjDfh8_SkckvoSsASxXllMkIX8GBNx...
And i use verify function in firebase-admin lib for verify token was send by client, but until token has expired(1h), i can't use this token.
How i get new accesstoken when current access token has expired?
You don't have to necessarily have to read user's idToken, store it anywhere or refresh it yourself. Instead you just need to use getIdToken() to get user's ID Token. If the token has expired, Firebase SDK will refresh and return a new token or return existing one. You can use getIdToken() before every request to your API.
const userToken = await firebase.auth().currentUser.getIdToken();
// pass userToken to in the API request
// API request here
You can use the following to obtain the oauth access token:
const provider = new GoogleAuthProvider()
provider.addScope(...)
...
const result = await signInWithPopup(auth, provider)
const credential = GoogleAuthProvider.credentialFromResult(result)
console.log(credential.accessToken)
I don't know how to refresh that token.
I am using firebase auth and functions for my project. My database api is with a different provider. I need to make some calls to my database from functions as "admin". My server is set up to verify firebase's jwt tokens via following config (custom verification, can't use firebase admin):
{
"type":"RS256",
"jwk_url":"https://www.googleapis.com/service_accounts/v1/jwk/securetoken#system.gserviceaccount.com",
"audience":"<firebase-project-id>",
"issuer":"https://securetoken.google.com/<firebase-project-id>"
}
This verifies ID Tokens correctly, however fails to parse custom tokens created by admin.auth().createCustomToken with following error:
Could not verify JWT: JWSError JWSInvalidSignature
Hence I can't use custom tokens to authenticate my cloud functions unless I can verify them somehow?
This is how my function token is generated:
const uid = "function-worker";
const claims = {
"https://hasura.io/jwt/claims": {
"x-hasura-default-role": "function",
"x-hasura-allowed-roles": ["function"],
"x-hasura-user-id": uid,
},
};
const jwt = await admin.auth().createCustomToken(uid, claims);
Generated jwt is then send to my hasura server as per https://github.com/hasura/graphql-engine/tree/master/community/sample-apps/firebase-jwt
Following guide above works for id tokens, but not for custom tokens. More detailed explanation for how hasura server handles jwt verification can be found here https://github.com/hasura/graphql-engine/blob/dcab20a5ee388ebd754a7828de1309a3a2e0eaee/docs/graphql/manual/auth/authentication/jwt.rst#generating-jwt-config
You can use the Firebase REST API to generate an id token, server side.
https://firebase.google.com/docs/reference/rest/auth
Generate an id token on firebase functions
1 - REST API
import fetch from 'node-fetch';
...
const customToken = await admin.auth().createCustomToken(user.uid);
const tokenURL = 'https://identitytoolkit.googleapis.com/v1/accounts:signInWithCustomToken?key=';
const response = await fetch(tokenURL + API_KEY, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
token: customToken,
returnSecureToken: true
})
}).then(r => r.json());
console.log(response.idToken);
2 - Firebase Client on Server
import firebase from "firebase/app";
import "firebase/auth";
admin.initializeApp();
firebase.initializeApp(firebase_config);
...
const token: any = await admin.auth().createCustomToken(user.uid)
.then((customToken: string) =>
// use custom token to get firebase token
firebase.auth().signInWithCustomToken(customToken)
.then((cred: firebase.auth.UserCredential) => cred.user?.getIdToken()))
.catch((e: string) => console.error(e));
From firebase's documentation
firebase.auth().signInWithPopup(provider).then(function(result) {
// This gives you a Google Access Token. You can use it to access the
Google API.
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
}).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
});
Is there anyway I can get the refresh token for google api using firebase authentication. I couldn't find anything about this problem in Firebase's documentation. I am also aware that the User object also contains a refreshToken. Can I use that refreshToken from firebase to generate a new access_token for google api ?
Firebase Auth is currently focused on AuthN and not AuthZ. They do not manage OAuth tokens on sign in. All OAuth refresh tokens are discarded and only the initial OAuth access token is returned. If you need a Google refresh token, or a Google access token continuously, consider using GApi library to get a Google ID token/access token and then sign in with that to Firebase.
function onGoogleSignIn(googleUser) {
var googleIdToken = googleUser.getAuthResponse().id_token;
firebase.auth().signInWithCredential(
firebase.auth.GoogleAuthProvider.credential(googleIdToken));
}
You will always have the ability to get a Google OAuth access token from the Google sign in library that way.
I authorized the calendar api in my google sign in auth, using the following code (Angularfire2):
let auth = new firebase.auth.GoogleAuthProvider();
auth.addScope('https://www.googleapis.com/auth/calendar');
this.afAuth.auth
.signInWithPopup(auth).then((data) => {
console.log(data); // nothing about calendar here
});
Is there any way to access authorized scopes using FirebaseAuth?
For example, access the calendar data after the user signs and authorizes the calendar auth.
If you check out the reference docs, you'll see that there are examples for each provider, which demonstrate how to obtain the third-party OAuth token:
// Using a redirect.
firebase.auth().getRedirectResult().then(function(result) {
if (result.credential) {
// This gives you a Google Access Token.
var token = result.credential.accessToken;
}
var user = result.user;
});
Once you have the third-party token, you can use that directly against their APIs.
I'm trying to wrap my head around the best way to handle the creation of a customToken with Firebase in a secure way.
This is what I came up with:
The user logs in on the client side with email and password. firebase.auth().signInWithEmailAndPassword(email, password)
Storing the current UID of the user in local storage. localStorage.setItem('uid', response.uid);
Get the JWT token of the current user. firebase.auth().currentUser.getToken(true) and store the token in localStorage localStorage.setItem('token', res)
Make a post call to the server and add the token to Authorization header and send the UID in the body. const headers = new Headers();
headers.append('Content-Type', 'application/json');
headers.append('Authorization', localStorage.getItem('token'));
this.http.post('/api/login', localStorage.getItem('uid'), {
headers: headers
})
On the serverside verify the token const authorization = req.headers.authorization; admin.auth().verifyIdToken(authorization). If valid set the UID this.uid = decodedToken.uid;
Now generate the custom token. Add the additionalClaims const additionalClaims = {
premiumAccount: true }; and call the createCustomToken function. admin.auth().createCustomToken(this.uid, additionalClaims)
Send the custom token back to the client res.status(200).send({
token: customToken
})
On the client side login with the customToken. firebase.auth().signInWithCustomToken(data.token)
Is this summary a good practice or are there better ways to handle this?