Oauth2 Implicit Flow with single-page-app refreshing access tokens
Asked Answered
C

4

30

I am using Thinktecture AuthorizationServer (AS) and it is working great.

I would like to write a native javascript single page app which can call a WebAPI directly, however implicit flow does not provide a refresh token.

If an AJAX call is made, if the token has expired the API will send a redirect to the login page, since the data is using dynamic popups it will this will interrupt the user.

How does Facebook or Stackoverflow do this and still allow the javascript running on the page to call the APIs?

Proposed Solution

Does the below scenario sound sensible (assuming this can be done with iframes):

My SPA directs me to the AS and I obtain a token by Implicit Flow. Within AS I click allow Read data scope, and click Remember decision, then Allow button.

Since I have clicked Remember decision button, whenever I hit AS for a token, a new token is passed back automatically without me needing to sign in ( I can see FedAuth cookie which is remembering my decision and believe this is enabling this to just work).

With my SPA (untrusted app), I don't have a refresh-token only an access token. So instead I:

  1. Ensure user has logged in and clicked remember decision (otherwise iframe wont work)
  2. Call WebAPI, if 401 response try and get a new token by the below steps...
  3. Have a hidden iframe on the page, which I will set the URL to get a new access-token from the Authorisation Server.
  4. Get the new token from the iframe's hash-fragment, then store this in the SPA and use for all future WebAPI requests.

I guess I would still be in trouble if the FedAuth cookie is stolen.

Any standard or recommended way for the above scenario?

Coss answered 7/5, 2014 at 12:33 Comment(2)
This is an interesting question but have little to no Angularjs relevance. I suggest you remove that tag.Diluvium
how many time after access token has expired?Unstoppable
A
-2

In Google o-Auth , the access token will only be valid for 1 hour, so you need to programmatically update your access token in each one hour, simple you can create web api to do so,you need to have a refresh token, and also that refresh token will not be expired , using c# code, I have done this.

 if (dateTimeDiff > 55)
            {
                var request = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/oauth2/v3/token");
                var postData = "refresh_token=your refresh token";
                postData += "&client_id=your client id";
                postData += "&client_secret=your client secrent";
                postData += "&grant_type=refresh_token";

                var data = Encoding.ASCII.GetBytes(postData);            
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = data.Length;
                request.UseDefaultCredentials = true;

                using (var stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                }
                var response = (HttpWebResponse)request.GetResponse();
                string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

            }

you need to save the last updated date time of the access token somewhere(say in database), so that , whenever you have to make a request , so you can subtract that with current date time , if it is more than 60 minutes , you need to call the webapi to get new token .

Adali answered 21/3, 2016 at 14:39 Comment(4)
Refresh tokens are for server-side clients. The questioner's client runs in the user agent, thus it should not use refresh tokens. Refer to Dominick Baier's presentation Securing Web APIs – Patterns & Anti-Patterns, especially from minutes 45.Shame
yeah, it should not use refresh token, refresh token is intended to use at server side. when access token is expires , the app will prompt for authentication again. So the above code will automatically create a new access token from refresh token at server side. The user will not prompt for login again .Adali
Refresh tokens and this approach won't work in this case as the question is concerned with javascript client using Implicit flow.Sterling
refresh tokens is not exposed to client , it is stored in the database and we are only making a new access token if the token is invalid. token exchange is done at server side . the above code i've posed c# web API codeAdali
S
6

I understand that your problem is that the user will experience an interruption when the access token has expired, by a redirection to the login page of the authorization server. But I don't think you can and should get around this, at least, when using the implicit grant.

As I'm sure you already know, the implicit grant should be used by consumers that can NOT keep their credentials secret. Because of this, the access token that is issued by an authorization server should have a limited ttl. For instance google invalidates their access token in 3600 sec. Of course you can increase the ttl, but it should never become a long lived token.

Also something to note is that in my opinion the user interruption is very minimal, i.e if implemented correctly, the user will only have to authenticate once with the authorization server. After doing that (for example the first time when also authorizing the application access to whatever resources the user controls) a session will be established (either cookie- or token based) and when the access token of the consumer (web app using implicit grant) expires, the user will be notified that the token has expired and re authentication with the authorization server is required. But because a session already has been established, the user will be immediately redirected back to the web app.

If however this is not what you want, you should, in my opinion, consider using the authorization code grant, instead of doing complicated stuff with iframes. In that case you need a server side web application because then you can keep your credentials secret and use refresh tokens.

Sacred answered 18/7, 2015 at 16:29 Comment(1)
I've asked a question to handle expired tokens, probably you can answer that.Elidaelidad
B
3

Sounds like you need to queue requests in the event that an access token expires. This is more or less how Facebook and Google do it. A simple way using Angular would be to add a HTTP Interceptor and check for HTTP401 responses. If one is returned, you re-authenticate and queue any requests that come in after until the authentication request has completed (i.e. a promise). Once that's done, you can then process the outstanding queue with the newly returned access token from your authentication request using your refresh token.

Happy Coding.

Borscht answered 16/3, 2016 at 2:19 Comment(1)
Never use refresh tokens in javascript clients - refer to my comment added to the answer.Shame
D
2

Not sure if I understand your question but,

I would like to write a native javascript single page app which can call a WebAPI directly, however implicit flow does not provide a refresh token.

Summarize facts,

refresh token is sometimes used to be a part of A: Authorization Grant

https://www.rfc-editor.org/rfc/rfc6749#section-1.5

and as you said in implicit flow you dont get back refresh token, but only in Authorization Grant part

https://www.rfc-editor.org/rfc/rfc6749#section-4.2.2

so you can get back refresh token when issuing access token (refresh tokens are always optional)

https://www.rfc-editor.org/rfc/rfc6749#section-5.1

With my SPA (untrusted app), I don't have a refresh-token only an access token. So instead I:

  1. Ensure user has logged in and clicked remember decision (otherwise iframe wont work)
  1. Call WebAPI, if 401 response try and get a new token by the below steps...
  1. Have a hidden iframe on the page, which I will set the URL to get a new access-token from the Authorisation Server.
  1. Get the new token from the iframe's hash-fragment, then store this in the SPA and use for all future WebAPI requests.
  1. SPA(you) have no idea if user selected remember decision. Its in AS direction and should be complete blackbox. Skip this step.

  2. You can try to use access token and wait for result, always.

  3. If access token has expired and you dont have refresh token, you still can create hidden iframe and and try to get new access token.

  4. Lets assume your AS provide option to remember decision and wont change it in future, then: your iframe will get new access token without user interaction, then you will get result back in some unknown time limit. Result can be checked by setInterval for read specific cookie or iframe postmessage. If you dont get back data in time limit, then one from following scenarios occured:

  • lag, AS is slow, connection is slow or time limit is too tight
  • user didnt select remember decision

In this case:

  1. show iframe with login

I consider scenario above as good practise if AS doesnt provide refresh tokens, but I also guess every AS like that wont provide remember option as well.

StackOverflow <---> Google scenario (I can only guess)

  1. User login, authorization request occured

  2. User logs in, SO gets access token

  3. SO tries to use access token

  4. SO gets back result + refresh token

  5. SO saves refresh token

  6. SO has permanent access to users Google account

Dossier answered 1/6, 2015 at 10:38 Comment(2)
You will not get refresh token when issuing the access token everytime unless it is the first time.Refresh token will only give once, we need to store that refresh token. And using this refresh token we can call for new access token any time thereafter.Please refer my code above.Adali
@Adali feel free to edit my answer then. It is more then year old and Im currently out of topic.Dossier
A
-2

In Google o-Auth , the access token will only be valid for 1 hour, so you need to programmatically update your access token in each one hour, simple you can create web api to do so,you need to have a refresh token, and also that refresh token will not be expired , using c# code, I have done this.

 if (dateTimeDiff > 55)
            {
                var request = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/oauth2/v3/token");
                var postData = "refresh_token=your refresh token";
                postData += "&client_id=your client id";
                postData += "&client_secret=your client secrent";
                postData += "&grant_type=refresh_token";

                var data = Encoding.ASCII.GetBytes(postData);            
                request.Method = "POST";
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = data.Length;
                request.UseDefaultCredentials = true;

                using (var stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                }
                var response = (HttpWebResponse)request.GetResponse();
                string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();

            }

you need to save the last updated date time of the access token somewhere(say in database), so that , whenever you have to make a request , so you can subtract that with current date time , if it is more than 60 minutes , you need to call the webapi to get new token .

Adali answered 21/3, 2016 at 14:39 Comment(4)
Refresh tokens are for server-side clients. The questioner's client runs in the user agent, thus it should not use refresh tokens. Refer to Dominick Baier's presentation Securing Web APIs – Patterns & Anti-Patterns, especially from minutes 45.Shame
yeah, it should not use refresh token, refresh token is intended to use at server side. when access token is expires , the app will prompt for authentication again. So the above code will automatically create a new access token from refresh token at server side. The user will not prompt for login again .Adali
Refresh tokens and this approach won't work in this case as the question is concerned with javascript client using Implicit flow.Sterling
refresh tokens is not exposed to client , it is stored in the database and we are only making a new access token if the token is invalid. token exchange is done at server side . the above code i've posed c# web API codeAdali

© 2022 - 2024 — McMap. All rights reserved.