I managed to get the access_token and refresh_token on front end and now its stored in a database, On the backend .netcore application I create googlecredential with
var cred=googleCredential.fromAccessToken(access_token);
but this one doesn't refresh on its own and I have no idea how to refresh this. I can send a post request to their rest api and refresh it but I need to know when "creds" has expired to send that refresh request. PS know that the tokens are in a database so using anything but .fromaccesstoken is not an option.
EDIT:
Complete code can not be shared but here's how the application's flow is going. On a reactJS application I get the authorization code via react-google-login module
<GoogleLogin
clientId="xxx"
buttonText="Login"
onSuccess={this.responseGoogle}
onFailure={this.responseGoogle}
cookiePolicy={"single_host_origin"}
scope="https://www.googleapis.com/auth/drive.readonly"
accessType="offline"
prompt="consent"
responseType="code"
/>
this returns an authorization code as the only response body, this response is then used with another endpoint to exchange for refresh and access token at front end, these tokens are sent to a backend endpoint which stores them in a db. Now the server side can create a googlecredential object via
this.googleCredentials=GoogleCredential.FromAccessToken(this.googleAccessToken);
Only problem being this doesnt handle the refreshing on its own so you have to somehow implement it.
Since I can't find another way to refresh when GoogleCredentials object is created via fromtoken(ACCESS_TOKEN) what I've done is sending a post request with the refresh token that I have to the rest endpoint at the backend and get a new access_token.
Another problem faced was finding out when the access token was expired for which no solution could be found. But it returns a 401 exception at creating the drive folders via drive service in my case
var res = await this.driveService.Files.Create(folder).ExecuteAsync()
here res will raise an exception and I just put it in a try block and got the new access token via http request in catch and ran the statement again (hint: recursion)
Hope this helps anyone, but better proposed solutions will be welcomed. Please understand that this is going to be a backend solution reading refreshtoken from database.
Related
I’m writing a web api that will be called from a background service to fetch some data. After some research I decided to use a Json web token to achieve that but I’m still a bit confused regarding when a new token should be requested.
Let’s say I start up my service, I request a token, the token expires after 15 minutes, then after 20 minutes I make an api call with the expired token. I will get an unauthorized error or something.
My question is: How will the client know when to request a new token? Should it request a new one before every api call? Seems like I’m missing something. Maybe I should make the token permanent and store it in the database?
Thanks
The answer to this is slightly application specific, but the OAuth specification has a mechanism for "refresh tokens", which can be used to grant new "access tokens" (the token typically included on each API request), without having to send the user to the UI authentication process to have them re-authenticate. So, once you request an access token, you will receive a refresh token and an access token. This methodology allows access tokens to be used for much shorter time frames.
This can also be done without refresh tokens, but in those cases the access token timeout would likely be longer, and then you would request that the user re-authenticate through the usual OAuth UI process. Note that even when you do have refresh tokens, the refresh token can also be set to expire, in which would then require a user re-authentication through UI again.
In some API's you just make the API request as usual, and if you get a response that is defined by the API to be one that indicates the access token has expired, you can then issue an API call to refresh the token (or fully request a new one if that is expired, or you the API doesn't have refresh tokens), and then make the original API call again with the new access token.
The API can also have a response that includes the timeout or expiration date/time of the access token as well. Then, the client can avoid sending the initial API call first, and simply send the refresh token call first.
In implementing your API, you could likely use any of these methodologies.
Here's some general discussion on the OAuth spec website, to provide more depth:
https://www.oauth.com/oauth2-servers/making-authenticated-requests/
https://www.oauth.com/oauth2-servers/access-tokens/access-token-lifetime/
https://www.oauth.com/oauth2-servers/access-tokens/refreshing-access-tokens/
And also, here's an example from the Twitter API regarding response codes showing one of the access token expiration techniques (see the "Error Codes" section, under error code 89, which implies the token has expired and you need to get a new one):
https://developer.twitter.com/en/docs/basics/response-codes
Since your client is background service , you can use the Oauth2 Client Credential Flow . Your background service can request an access token using only its client credentials when the client is requesting access to the protected resources under its control.
With this flow , you does't need to care much about the token expires , if client sends an expired token to web api , web api validate the token and create token expires response to your service , your service check the status code/response , directly send a new token request to web api to get new access token , there is no need to use refresh token which uses in other flows .
The fact is that your harness should be prepared to request any token when getting an Unauthorized status code. What I do in test is to check the expiration datetime, if close enough I refresh or get a new token whatever applies to your Auth. Also when getting an unauthorized status code my code does a refresh once and keep a count. If I get another unauthorized code then I return a false or throw an exception after I log the error on the second try. This works fine for me.
I am trying to renew my external providers OAuth access tokens after they expire. They are stored in my SQL database via the signin manager once the user has logged in using the external provider originally.
The access token is used by the server to make requests on the users behalf (mostly to update my database with some external providers information every now-and-then).
I am using a .NETCore 3 preview-6 Angular starter project with built-in Identity Server (I realize this a preview - but I think my issue is more related to a lack of understanding what I should be doing!). I haven't had too much trouble getting the original external login working (using .AddAuthentication().AddOAuth(...)).
But when my service that is using the provided access token gets a 401 response, I cannot figure how to get my server to renew the access token for the user silently.
I have tried using [Authorize(AuthenticationSchemes = "MyScheme")] on my Controller but this has the external provider responding with a CORS error. I then tried HttpContext.ChallengeAsync(...) in my service, but that just didn't even seem to work at all (maybe this is related to the preview version of .NETCore?).
I have even tried to re-create the scaffolded code that returns a new ChallengeResult(...), but I get the same CORS error when called from my API. (I am unsure what black-magic is going on here to make the request appear like the referrer is the external provider.)
TL;DR (code snippet)
Ideally, I am trying to make the // TODO section work:
// This code is inside a Service I have which is called by a Controller action
var user = await _userManager.GetUserAsync(_httpContextAccessor.HttpContext.User);
var accessToken = await _userManager.GetAuthenticationTokenAsync(user, "MyScheme", "access_token");
_client.SetBearerToken(accessToken);
var response = await _client.GetAsync($"{uri}?{_query.ToString()}");
if (!response.IsSuccessStatusCode) // Just assuming 401 for simplicity
{
// TODO: renew the access token and try again
}
I am expecting to get an updated access token back that I can use to re-do the request. Ideally this would be a silent renew, or a redirect if the requested external access has changed.
Hope this makes sense, please let me know if you need more information.
Thanks
So, I've basically got this working, except for one issue. I've got a google service account set up so it can access our domain contacts. And it can batch query them perfectly!
But if I call cr.Retrieve("some-contact-url-here"), it throws an error griping about not having a Refresh token. I'm using a service account though, so I don't get a refresh token when I authenticate.
And I can't seem to find any good answer as to how I'm supposed to get a refresh token for a service account. There's one or two stackoverflow posts which actively mention getting a refresh token with a service account....but what they linked to has since been redirected. Anything else I've found to do with refresh tokens has basically been about authenticating manually and storing the token. Because I need to use a Service Account, that is not a possibility.
A service account's credentials, which you obtain from the Google Developers Console, include a generated email address that is unique, a client ID, and at least one public/private key pair. You use the client ID and one private key to create a signed JWT and construct an access-token request in the appropriate format. Your application then sends the token request to the Google OAuth 2.0 Authorization Server, which returns an access token. The application uses the token to access a Google API. When the token expires, the application repeats the process.
Check this page for more information.
Ok, based on several hours of bashing my head violently against the API, it looks like there's basically no way to get a refresh token when you're authenticating as a Service Account. Expected behavior, really.
Anyway, to get around the issue, you can load all of the contacts into memory,
feed.AutoPaging = true;
foreach (var c in feed.Entries)
{
contactList.Add(c);
}
And you can update|delete|etc then. Grossly inefficient though. Especially if your contact list gets rather big.
I need help to obtain an authorize code, is there any way to get one without accepting anything or any window that pops up. I need this for my service as an automated process.
I tried like a thousand ways but nothing works.
Please does anyone know a solution?
As per the docs (http://msdn.microsoft.com/en-us/library/dn659750.aspx), you can use a refresh token if your app has offline access in its scope.
If offline access is set, when you retrieve your authentication token normally, you'll also get a refresh token. When the normal token expires, you can request a new one similarly to the first token, replacing the code query parameter with the refresh_token parameter.
To be clear, the process looks like this as per the docs:
Send your user to your web service
Direct them to the OneDrive authorise page with the correct parameters (make sure offline access is in the scope)
Wait for them to be redirected back to your app with the authorisation code
Exchange the authorisation code with OneDrive (using the oauth20_token.srf endpoint) to receive a set of tokens (one of these will be refresh)
Wait for the access_token you received to expire
Exchange the refresh_token you received for a new access token as per the "Getting a new access token or refresh token" section of the docs
I've created a web application that uses the OAuth authentication and universal connectors as explained in this tutorial, and started to fiddle around a little to add support for other providers like Yahoo and LinkedIn. So the authentication part works and users are created in the asp.net Membership provider. Also, all the providers return the accesstoken which I supposedly can use to retrieve more information regarding the user.
I'd really like to acquire the profile image, but it seems every provider has a different way of requesting this information. Twitter even describes a way to authorise every request by changing the HTTP header information.
Whilst reading this information on the websites of the various providers I was wondering whether this functionality isn't also already included somewhere in DotNetOpenAuth.AspNet or Microsoft.AspNet.Membership.OpenAuth implementation.
How can I use DotNetOpenAuth.AspNet and/or Microsoft.AspNet.Membership.OpenAuth to request the profile image of the loggedin user using the just acquired accesstoken?
UPDATE in response to Leo's answer
I use the following code to make a call on LinkedIn's API.
string accessToken = extraData["accesstoken"]; // Extra Data received from OAuth containing the accesstoken.
WebRequest request = WebRequest.Create("https://api.linkedin.com/v1/people/~:(id,first-name,last-name,date-of-birth,email-address,picture-url)?oauth2_access_token=" + accessToken);
using (WebResponse response = request.GetResponse())
{
// do something with response here.
}
Error message is "The remote server returned an error: (401) Unauthorized.".
What am I doing wrong?
The answer is simple...you can't use any of these. These are wrappers of OAuth and OAuth only specifies how you can authenticate a user. Now, to request the user's profile photo you will need to use the external provider's own API and you will need most likely a valid access token. So, you will need to use one of these implementations of OAuth to authenticate a user and the recieve an access token, store the access token somewhere (usually a cookie) and then use the access token to make sub-sequent calls to the provider's APIs. Examples and links....
Facebook's Graph API allows you to retrieve users profiles
https://developers.facebook.com/docs/graph-api/quickstart/
notice that all examples in the link above will require you to include the access token in a parameter named access_token, for example
https://graph.facebook.com/me?method=GET&format=json&suppress_http_code=1&access_token={your-access-token}
Google...
https://www.googleapis.com/oauth2/v3/userinfo?access_token={your-access-token}
LinkedIn...
https://api.linkedin.com/v1/people/~:(id,first-name,last-name,date-of-birth,email-address,picture-url)?oauth2_access_token={your-access-token}
You can get more specific information from these providers' websites
Let me know if you have any other doubts I might be able to help you since I have implemented stuff like these before.
Cheers, Leo