Incremental authorization with Firebase and GoogleAuthProvider - firebase

I'm using Firebase v8 with the GoogleAuthProvider.
Firebase documentation provides the following code to authenticate the user.
firebase.auth().signInWithPopup(provider).then((result) => {
/** #type {firebase.auth.OAuthCredential} */
var credential = result.credential;
// This gives you a Google Access Token. You can use it to access the Google API.
var token = credential.accessToken;
// The signed-in user info.
var user = result.user;
// ...
})
Questions
Google's Using OAuth 2.0 to Access Google APIs article recommends incremental authorization (it's not Firebase, but the recommendation is clear)
It is generally a best practice to request scopes incrementally, at
the time access is required, rather than up front. For example, an app
that wants to support saving an event to a calendar should not request
Google Calendar access until the user presses the "Add to Calendar"
button.
AFAICT, there is no way to achieve incremental authorization with Firebase without re-authenticating the user. While scopes can be added to GoogleAuthProvider using addScope, a subsequent call to signInWithPopup is required (i.e. the user is re-authenticated). Is there any way to prompt only for authorization (e.g. Drive access) without re-authenticating?
Assuming the access token is short lived, can the Google ID token be used to obtain a new access token? Is re-authenticating the user the only way to obtain a new access token?
Is there a way to determine whether the access token has expired?

Related

Firebase admin - get Google OAuth token

I have a web application where users can sign in with Google.
To the sign-in process, I add a scope to be able to access Google Calendar.
Now that the user is signed in, I would like to - in server-side - get their current Google access token in order to make a request and get a list of their events.
Is there a way to get the current OAuth token (no need for refresh token) in order for me to make this completely on the server-side?
I'd say that you can check this article and put special attention to the recommendation for websites.
I understand you have configured already the consent screen, which is the first step of the basic steps on using OAuth 2.0. So I understand that you only have to perform the following steps:
Obtain an access token from the Google Authorization Server
Examine scopes of access granted by the user.
Send the access token to an API
I think you can also give a look to this other doc for more GCP insights over your goal to authorize the request using user tokens
Edited:
Regarding the Firebase Authentication, I understand this happens at the user's device, and you could use some code to retrieve the token and then send it to your back end servers as mentioned in here.
As a sample here there's the sample code for retrieving the token in Android:
FirebaseUser mUser = FirebaseAuth.getInstance().getCurrentUser();
mUser.getIdToken(true)
.addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
public void onComplete(#NonNull Task<GetTokenResult> task) {
if (task.isSuccessful()) {
String idToken = task.getResult().getToken();
// Send token to your backend via HTTPS
// ...
} else {
// Handle error -> task.getException();
}
}
});
A little about OAuth 2.0
Whenever a user signs up to your app/website via Google or 3rd Party, an Authorization Code, this Authorization Code is exchanged for an AccessToken & RefreshToken.
The AccessToken sent via Google are valid generally for 60 minutes.
Offline Access (Server Side)
Let's break it down to two parts:
If your need to update within 60 minutes of user's last activity
You can use firebase along with gapi to achieve that. You'll be provided with the AccessToken that can be sent back to server to add to calendar.
More info on implementation
If you need to update after 60 minutes of user's last activity
Firebase & gapi's most method handle the AuthorizationCode flow internally. They even further refresh the AccessToken after 60 minutes. This is beneficial for most developers as they won't have a headache of managing all the tokens.
This method but, hides RefreshToken & AuthorizationCode from the developer. That is even if your server has the access token, it won't be able to refresh it and it would be deemed useless.
To achieve complete offline access, in the initial request to get AuthorizationCode you will need to send a HTTP GET parameter access_type to offline
GAPI provides you with grantOfflineAccess() method which returns the AuthorizationCode that can be later used on your server to fetch access token & refresh token.
Note: If you are storing AuthorizationCode in your database, make sure it is secure. The limitation in Firebase are set due to security reason. It is more secure to not talk with AuthorizationCode generally.
More links
https://developers.google.com/identity/protocols/oauth2/web-server
https://developers.google.com/identity/sign-in/web/reference
https://developers.google.com/identity/sign-in/web/server-side-flow
https://developers.google.com/identity/sign-in/web/backend-auth
Retrieve Google Access Token after authenticated using Firebase Authentication

How can I create long-lived tokens with Firebase Node.js SDK

I'm trying to implement the Authorization Code Flow to link actions on google with my firebase users:
https://developers.google.com/actions/identity/oauth2-code-flow
So far I've understood the flow as follows:
1 - User access the application and is redirected to the authorization url endpoint
2 - User signs in and google receives an authorization token
3 - Google sends this authorization token to the token url endpoint and gets an access token a refresh_token and a expiration time
4 - Google sends the refresh token to get a new access token when the access token is going to expire and gets a new acess token and a new expiration time
Did I get everything right?
As authorization token and access token I'm using the custom tokens from Firebase. My question is, how can I implement the refresh token? I cannot get this token from the Firebase Node.js SDK server side.
How can I greate long-lived tokens with Firebase Node.js SDK?
Is there another approach?
Yes, you got the OAuth2 process right.
The Firebase Admin SDK lets you sign the user in to your Firebase service using generated custom tokens. Though the custom token expires within 1 hour, once user is signed-in, they should be authenticated indefinitely (i.e. until user signs out). As such, there is really no need for SDK to generate refresh token.
I'd suggest a different approach. Use Actions on Google's SignIn helper intent to get user's info, such as email, name etc. Using this info, you will be able to sign the user in to Firebase as follows (referenced from the "Create Custom Token" Firebase doc):
var uid = "some-uid";
admin.auth().createCustomToken(uid)
// token == custom token
.then(function(token) {
firebase.auth().signInWithCustomToken(token).catch(function(error)
{
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
})
.catch(function(error) {
console.log("Error creating custom token:", error);
});
References:
"How to use refresh token?" from Firebase's GitHub
"Create custom tokens" from Firebase's docs
"Request Signin helper" from Actions on Google docs

What format is the Google Picker API auth token?

If I intercept an access_token from an example on this page:
https://developers.google.com/picker/docs/
...it looks like this:
ya29.Glx7BW_OsFJ1CSjJ_kdt9iZixJAZHjvllMtJO09EccpURJPbCXudNP6teBz6HZ0T_ioaQLNna323UPpBQN-N0aCLWXfDVCvq4xXkbi5kKQhfwS0xakcsrNmzD5B8OA
If I paste that into here:
https://jwt.io/
...I'm told it's malformed. However, if I paste a Firebase auth token into there, it's well-formed and you can see its payload.
Ultimately, I'm trying to use the Firebase auth token in the Google Picker API, but I can't figure out how to translate it.
I was using firebase.auth().currentUser.getIdToken, but it looks like firebase.auth().getRedirectResult() gets an auth token that is the same format as the Picker API.
Google API access is scope based https://developers.google.com/identity/protocols/googlescopes
You must specify the scopes needed when requesting an access token.
Each access token you receive back, is limited to the scope and application from its original request. They are not inter-changeable.
You will need to setup a specific Google Picker API request, in order to get the access token you require https://developers.google.com/identity/protocols/OAuth2
Hope this helps
"ya29.*" token is Google's access token and it is not a jwt (it is opaque for the api callers).
Yes firebase allows you to add scopes during the sign-in process. After that you get an id token that says who the user is and a separate access token that you can use to access Google APIs.
If you look at https://firebase.google.com/docs/auth/web/google-signin you will see this:
Then, you can also retrieve the Google provider's OAuth token by calling getRedirectResult when your page loads:
firebase.auth().getRedirectResult().then(function(result) {
if (result.credential) {
// 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;

Get Google access token

To get Google access token after firebase auth login, I know I can simply do this:
firebase.auth().signInWithPopup(provider).then(function(result) {
var token = result.credential.accessToken;
}
but what if the user is already authenticated and I need the token? is there any way to extract it from the Firebase auth?
I've been through every value of authState but I couldn't find the google access token I've been looking for.
You can't get the access token from the onAuthStateChanged listener or the currentUser. You can only get it immediately after authentication when calling signInWithPopup, reauthenticateWithPopup, linkWithPopup, getRedirectResult, etc. Firebase Auth does not manage OAuth tokens for users. If you feel strongly about this feature, please file a feature request for it on the Firebase forum: https://groups.google.com/forum/#!forum/firebase-talk
You can also just use the GApi library to get the Google access token and pass it to Firebase to sign-in via signInWithCredential. The advantage here is that GApi will manage that OAuth token for you.

Does Firebase support validating a pre-existing facebook access token?

Suppose, for the sake of argument, that I already have a facebook access token for a user of my application. In that case, I don't really need to go through Firebase's whole auth.login("facebook") process, I really just want a trusted server to make sure this is a real access token (e.g. by making a GET request to "https://graph.facebook.com/me" with it) and then to set the Firebase user ID appropriately. Can Firebase do this?
Firebase Simple Login was recently updated to support logging in with an existing Facebook access token.
This means that you can integrate directly with then native Facebook JS SDK in your application, and then pass that Facebook access token to Firebase Simple Login (skipping a second pop-up) via:
var ref = new Firebase(...);
var auth = new FirebaseSimpleLogin(ref, function(error, user) { ... });
auth.login('facebook', { access_token: '<ACCESS_TOKEN>' });
See the access_token option on https://www.firebase.com/docs/security/simple-login-facebook.html for more information.

Resources