Why Google OAuth Api Refreshes Token? - google-oauth

I am trying to get the Access Token using google oauth 2.0 but whenever i login again it goes through the consent screen process and generates a new token.
Is there any way to get same token until the token expires? or(2)
How to stop google from asking to allow to access scopes, if the user has accepted/allowed once.
This is how i am getting the access token.
Uri.https("accounts.google.com", '/o/oauth2/auth', {
'response_type': 'code',
'client_id': identifier,
'redirect_uri': '$REDIRECTURL',
'scope': 'https://www.googleapis.com/auth/youtube.readonly',
});
then:
response = await http.post("https://oauth2.googleapis.com/token", body: {
'client_id': identifier,
'redirect_uri': '$REDIRECTURL',
'grant_type': 'authorization_code',
'code': code,
});

I'm afraid that's how tokens work: whenever you ask one, you get a fresh one. Just store the access and refresh token (needed to refresh the access token when it expires) on your side and don't initiate the OAuth if it's not needed.

There are parameters setApprovalPrompt = auto / force
Force will cause that it will ask access scopes every time.
Auto won't ask, but you will not get "refresh_token" if you already received it before, and you only get "access_token" that is valid for 1 hour

Related

How to get a new access token via refresh token using Expo Auth Session?

I'm using Google for auth (expo-auth-session/providers/google)
I can successfully login for the first time and fetch an access token and a refresh token. The refresh token will get stored in SecureStorage.
Now at this point, when the old access token is invalidated, I need to use the refresh token to get a new access token, but Expo's docs don't really provide any guidance on this part. I have checked their API quite thoroughly, but can't see anything that helps me retrieve a new access token with a refresh token.
Any guidance would be welcome.
The AuthSession library has a method specifically for refreshing tokens. It requires the clientId used to retrieve the token initially, so you can reuse that, the refreshToken which you have stored as well as a token endpoint.
const tokenResult = await AuthSession.refreshAsync({
clientId: "<your-client-id>>",
refreshToken: "<your-refresh-token>",
}, {
tokenEndpoint: "www.googleapis.com/oauth2/v4/token",
},
);
I wasn't able to test this myself as we use firebase and useIdTokenAuthRequest as a result, so I wasn't able to get my hands on a refreshToken to run it through that function - but the documentation of that method seems pretty solid.

blacklisting/validating/generating JWT Refresh Tokens

I issue an access token along with a refresh token upon successful login. They are both saved in same site cookies in the browser. A custom middleware will put the token in Authorization header before the authentication process. This middleware will also check if the access token is expired, if it is it will try the refresh token, if validated it will save two new cookies(the new refresh token and new access token) and pass the new generated access token with the current request.
Is this how we are supposed to implement refresh tokens? If I want to blacklist a specific refresh token, should i save all refresh tokens in the database?
string auth = httpContext.Request.Cookies["AuthToken"];
if(string.IsNullOrEmpty(auth))
{
httpContext.Request.Headers.Add("Authorization", $"AuthorizationCookieNotFound");
return _next(httpContext); //That token wont be accepted i just
// put it there for the sake of demonstration
}
httpContext.Request.Headers.Add("Authorization", $"Bearer {auth}");
return _next(httpContext);
You have to be very careful while storing refresh tokens and they must be kept at some secret place otherwise you know the consequences.
I assume you are using "Aurhorization Code flow" here. So it's a good idea to store the refresh tokens in the db against the username and you can add an extra column e.g "IsRevoked" for status purpose and then you can blacklist/whitelist the tokens basked on the username and isrevoked status.
See this link about storing tokens.

jwt payload is the same each time i register a new user

i'm new to this . i've followed along with this tutorial //https://devdactic.com/restful-api-user-authentication-1/
so it's all working , adduser is hashing , login is returning a jwt login a new user is returned to jwt.encode in route /api/login and a new jwt is returned
I want to get the user details from a protect route, for example
server.get('/api/routeThatNeedsJWTToken', passport.authenticate('jwt', { session: false }), function(req, res){
res.json({'Success! You can not see this without a token':'bla', user: req.user});
});
Here the req.user returned is always the first user i registered. i need the one matching the jwt, the users id or name .
any help appreciated or i'm thinking about starting from scratch and going this way https://www.sitepoint.com/using-json-web-tokens-node-js/
please see server.js and config/passport.js for a walk through the code
https://github.com/Lambda-School-Labs/decisionjam
here is an example of two jwt
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjaG9pY2VzIjpbXSwiY3JlYXRlZE9uIjoiMjAxOC0wNS0xMFQxMjozMjo1MS45MTJaIiwiX2lkIjoiNWFmNDNiZjNmZmNmNGNmM2JjMjBlMmFjIiwidXNlcm5hbWUiOiJwYXQxIiwiZW1haWwiOiJwYXRlbWFpbDEiLCJwYXNzd29yZCI6IiQyYSQxMCR2eERIc2RkckYxZUFDdURIODdTNlFPYlZVcTlPcUtoNmV1cmRkQWpvWTVXbkRFaXRwbGJqYSIsIl9fdiI6MH0.vUcvRiJJD5s8kWBIodbE5ZCQeRdn0r7m6b1pC0KWnYk"
JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjaG9pY2VzIjpbXSwiY3JlYXRlZE9uIjoiMjAxOC0wNS0xMFQxMjo1MjoyNy45MThaIiwiX2lkIjoiNWFmNDQwOGI4MDA2YWJmNGUxZGU5ZmIxIiwidXNlcm5hbWUiOiJwYXQyIiwiZW1haWwiOiJwYXRlbWFpbDIiLCJwYXNzd29yZCI6IiQyYSQxMCRTbGhqTHg0aXBlSzRRd2h5T0FSajRPaHZZSWpyWG1rVmFjUjdYL09kMlBpdldoTG5lcUtzSyIsIl9fdiI6MH0.NmZrAupocchKjvwUBblOpLBIFmMujaF2gZ9ii_YfK48"
In req.user is the user saved in session. If you want to take user from JWT token, you should add an authentication method in path declaration, that extract info from JWT token, with jwt.decode and assing the result to the req.user.
This way user object in request will be the user being authenticated by the token.

OAuth Implicit flow Access Token expires every hour

I'm having a problem with the OAuth Implicit flow for the Google Assistant.
I managed to set up a OAuth server and got it to work. Here's the flow:
The user Is redirected to my endpoint, authenticates with a Google account, and gets send back to the Assistant with an Acces Token and result code=SUCCES.
In my fullfilment I get the users email address by doing a https request to: https://www.googleapis.com/plus/v1/people/me?access_token=access_token.
I then find the matching user in my database and add the acces token to the database for this user.
The next time the user logs in I check the acces token and greet the user by their name.
Now the problem is that this is the Implict flow which according to the documentation should have an access token that never expires:
Note: Google requires that access tokens issued using the implicit
flow never expire, so you don't need to record the grant time of an
access token, as you would with other OAuth 2.0 flows.
But the Assistant forces me to re-authenticate every hour, meaning the access token did expire.
My question is: Is this flow correct or am I missing something? Is there something I've done wrong in my OAuth endpoint?
I based my endpoint on https://developers.google.com/identity/protocols/OAuth2UserAgent.
<html>
<head>
<script src="https://apis.google.com/js/platform.js" async defer></script>
<meta name="google-signin-client_id" content="CLIENT_ID">
</head>
<body>
<script>
var YOUR_CLIENT_ID = 'CLIENT_ID';
function oauth2SignIn() {
// Google's OAuth 2.0 endpoint for requesting an access token
var oauth2Endpoint = 'https://accounts.google.com/o/oauth2/v2/auth';
// Create element to open OAuth 2.0 endpoint in new window.
var form = document.createElement('form');
form.setAttribute('method', 'GET'); // Send as a GET request.
form.setAttribute('action', oauth2Endpoint);
//Get the state and redirect_uri parameters from the request
var searchParams = new URLSearchParams(window.location.search);
var state = searchParams.get("state");
var redirect_uri = searchParams.get("redirect_uri");
//var client_id = searchParams.get("client_id");
// Parameters to pass to OAuth 2.0 endpoint.
var params = {
'client_id': YOUR_CLIENT_ID,
'redirect_uri': redirect_uri,
'scope': 'email',
'state': state,
'response_type': 'token',
'include_granted_scopes': 'true'
};
// Add form parameters as hidden input values.
for (var p in params) {
var input = document.createElement('input');
input.setAttribute('type', 'hidden');
input.setAttribute('name', p);
input.setAttribute('value', params[p]);
form.appendChild(input);
}
// Add form to page and submit it to open the OAuth 2.0 endpoint.
document.body.appendChild(form);
form.submit();
}
oauth2SignIn();
</script>
</body>
</html>
It sounds like what you are doing is having the user log into your page, and using this to get an auth token from a Google service. You're then turning this around and passing this back to the Assistant and calling this the Identity Flow.
While clever - this isn't the Identity Flow.
This is you using the Auth Code Flow to authenticate the user with Google, and then returning this token to Google and pretending this is an Identity Flow token. However, since you're using the Auth Code Flow, the auth tokens that you get back expire after an hour. (You can check out the lifetime in the information you get back from Google.)
If you are trying to do Account Linking and not manage anything yourself, you need to actually implement an OAuth server that proxies the Auth Code Flow requests from the Assistant to Google and the replies from Google back to the Assistant. While doable, this may be in violation of their policy, and isn't generally advised anyway.
Update to address some questions/issues in your comment.
using the Google Auth endpoints doesn't store the session either, so you'd still have to re-authenticate every hour
Since the Google Auth endpoints use the Auth Code Flow, you can use the offline mode to request a refresh token. Then, when an auth token expires, you can use the refresh token to get a new auth token. So you still have a long-term authorization for access and can get the short-term token to do the work you need.
Trying to shoehorn this into the Identity Flow, however, doesn't work. (And would be a really bad idea, even if it did.)
Can you provide some clarification on how to create an endpoint for the implicit flow?
Beyond the step-by-step description of what your OAuth server code can do in the Assistant documentation, I'm not sure what clarification you need. Your OAuth server fundamentally just needs to:
Be able to have a user:
Connect to an HTTPS URL
Authenticate themselves
Authorize the Assistant to contact your service on their behalf
Return a code by redirecting the user to Google's URL with a code in the parameter
And the Action webhook needs to be able to:
Accept this code as part of the request from the Assistant and
Figure out who the user is from this code. (ie - map the code to a user account in your system.)
There are a variety of ways you can do all of that. The OAuth server and Action could be on the same server or separate, but they at least need to have some agreement about what that code is and how that maps to your user accounts.
If your primary need is to access Google APIs on behalf of your user - then the user account that you have will likely store the OAuth tokens that you use to access Google's server. But you should logically think of that as separate from the code that the Assistant uses to access your server.
(As an aside - those steps are for the Identity Flow. The Auth Code Flow has a few more steps, but the fundamentals are similar. Especially on the Action side.)

Issuing authenticated queries with Graphcool

I have successfully set up Graph.cool Auth0 authentication and created a User through Relay as described here.
Next I'd like to actually query graph.cool on behalf of this user. As a first step, I simply manually modified the Relay setup to specify the same auth token as was used to create the User in the first place (through the idToken on type AUTH_PROVIDER_AUTH0):
Relay.injectNetworkLayer(
new Relay.DefaultNetworkLayer(process.env.GRAPHQL_ENDPOINT, {
headers: {
Authorization: 'Bearer XXX.YYY.ZZZ',
},
})
);
However, the app stops rendering and I just get a console warning RelayPendingQueryTracker.js:153 Server response was missing for query Index. Any hints?
When calling the signinUser or createUser mutation, a Graphcool token is returned in the payload. https://www.graph.cool/docs/faq/graphcool-session-user-goij0cooqu
This is the token you need to use in the Authorization header instead of the Auth0 idToken.
Maybe it can also be a help to take a look at how we do it in the dashboard https://github.com/graphcool/dashboard/blob/master/src/views/LoginView/LoginView.tsx
Hope this helps!