After removing the JWT from local storage, why token is sent in the HTTP header for a node API call? When I go to the previous page using the browser's back button and perform any action( in angular 6).
Obviously the browser is caching the JWT for some reason. But, this should not really be a security issue for you, because typically your Node application would always have to verify that an incoming JWT is valid and can be trusted. The usual workflow for a request coming to your Node application would look something like this:
The browser sends a request to some endpoint in your Node application, including the JWT
The Node application receives the JWT
It might check the claims, but also
It would check for things such as the user's status, perhaps by hitting a database table
A JWT is not, and should not be treated as, a license to unconditionally access your Node application. Things like claims are independent of the browser state, so this caching issue should not be a real problem. And, most of the time, your Node app would want to run additional checks on the server side.
Related
I've successfully used cookies before and I'd like to begin using JWT. My biggest question is how to pass your token to a website during the initial GET operation, for example when a user types your domain into their address bar or clicks on a link from some other website like google.
When using cookies for example, if you type stackoverflow.com into your web browser, the persistent cookie is sent to the website which seamlessly allows your own stackoverflow session to be automatically authorized.
I am aware that I can programatically pass my JWT token via a javascript GET through the HTTP headers but how do you pass the token when a visitor types in your URL into their web browser?
Possible solution #1
My thoughts have been to have javascript code check if 'authorized'. If not, check for a JWT token in local storage. If found, redirect to the same address. The problem of course would be that there is no way to pass the token during a redirect.
Possible solution #2
Similar to above but not issuing a redirect, I would update the current page to reflect the 'authorized' state.
Possible solution #3
Use a permanent cookie containing the JWT token. I am thinking that this 3rd option would be the best. If I did this, there would be no need to pass the JWT via an HTTP header.
I've thought about this for a few days, read up on JWT and here are my conclusions for avoiding JWT in my particular case:
No easy way to authorize a user who opens their browser and types in your website. With cookies, your server immediately knows how to respond to this headerless GET request.
No way to easily change the JWT token signature. All users are immediately affected by such a change, essentially forcing everyone to authenticate again.
No way to easily invalidate a specific JWT token. The best you can do is to maintain and check a banned signature list on the server. This of course would require a centralized or distributed server method almost identical to a cookie session management system. This would force a coupling between the token and the server, no longer stateless as intended by JWT.
SUMMARY
Cookie management requires more server infrastructure but you have much greater session control. State is seamless (in the case of #1 above). You can instantly invalidate state. For example, the user can log out (deleting the session at the server level) or the session can be instantly banned by an administrator by simply deleting the session.
I also see the benefits to JWS:
no need to hit the db or cache system when authorizing.
simple authorization between multiple servers having the secret key.
simple authorization, no session management programming and no db session state storage required.
...but the drawbacks stated previously are too great for my particular needs.
I'm looking for a way to restrict user access to specific clients in a realm.
I know I can do it with client where Authorization is enabled (fine-grained authorization support) but it doesn't work when trying to connect from front (client need to be public and not confidential).
I'm using a javascript application to login from front-end.
Is there a way to enable Authorization for public client or a work around ?
Thanks.
I'm not sure if this will totally answer your question because it's still not specific enougth but it may give you some further help.
In case you're new to the topic, please see difference between public and confidential clients here.
The current best practice for public clients like HTML/Javascipt applications is to use OpenId Connect with the Authorization Code Flow + PKCE. HTTPS is of course a must have. I recommend you use a javascript openid connect adapter for this like the following for example:
https://github.com/panva/node-openid-client
Basically your authentication / authorization flow is shown here:
When the user wants to login from your frontend client application first a unique verifier is generated which is only available to the exact user / browser session. This value get's hashed as a code challege. Then the user gets redirected to the login page of your authorization server (Keycloak for example) passing some parameters like a redirect uri and the challenge.
With successful login the user get's a session at the keycloak server which also stores the hashed challenge. Then the user gets redirected to given redirect uri (a path in your application) together with a code to obtain an access token. Back in your application you application uses the original value together with the code to get the actual token. The authorization server ckecks the value against the stored challenge and geturns the access token if it matches. You see the extra verifier is to prevent that anybody compromises your code fragment to obtain a token on your behalf.
Now you have an encoded access token in your browser app. Note the token itself is normally only encoded not encrypted but it can be signed. Those signatures can later be used from your backend to ckeck the token integrity but we will come to that soon. Roles, claimes, scopes and so on included in your access token tell you the privileges of the user/identity. You can of course use them to enable/disable functions in your app, block routes etc. but in the end client protection is never really effective so your real authorization ande resource protection happens at your resource server which is your backend (node, .net core, java etc.) maybe a restful Web Api. You pass your access token as a part of the http request header with every request to the backend. Now your backend checks the token integrity (optional) expiration time etc. analyzes scopes, claimes and roles to restrict the resource access.
For example a simple GET myapi/car/{1} may only need a token or can even be annonymous while a POST myapi/cars or PUT myapi/car/{1} may need a special role or higher privileges.
Does that help you out?
Does using tokens for authentication break REST principles, which is supposed to be stateless.
I have an application which is required to be REST and I stored some tokens in a database. Each time a user wants to do an action, they should get a token (by sending a username and a password) and send it to the server with every request.
No they, don't.
A key aspect of something like the authentication header is the fact that it's orthogonal to the request itself. It's a property of the request in the same way that a Content-Type header is.
How Authentication is implemented on the back end is not germane to the discussion as long as results of the requests that submit the header are consistent. There's no reason the process of validating an authentication header can't be a stateless process in and of itself.
The presence and content of the Authentication can certainly impact what a client receives from a request, from a 403 Unauthorized response, to a limited amount of content based on whether the client is, perhaps, using an "admin" token vs. a non-privileged user.
It's also in contrast to a Cookie, which represent Session state (which is not RESTful). This is because the two headers serve different purposes and offer up different application semantics.
Authentication Tokens are a standard way of authenticating REST Clients.
Authentication token themselves do not beak REST principles as long as your API doesn't behave differently based on the Auth token passed to the API.
i.e. if 2 consumers place the same request with different auth token, and they are both allowed to perform that operation, the result should be the same.
You can find more info on REST API authentication here: https://dzone.com/articles/api-security-ways-to-authenticate-and-authorize
No it does not break the rule of being stateless.
Why?
Because the server is not maintaining any session w.r.t the client. It is just validating the token provided by client and returning results based on that.
If its client that has to maintain any data related to the session (which happens in case of tokens since they are sent with every request) then it is not breaking the REST principle, it is still stateless since the server is not maintaining the session or data related to the session.
Hope that helped.
It does break Rest principles because once the service generates a temporary token based on login credentials, the service is no longer stateless. The service has to check with itself if the token has expired yet (the token is part of the system state now), for each call made using that token.
One can't say the session at any point is independent of all the client's previous actions, because if they did not log in correctly, they could not even use the system.
But you should use logins and tokens, and break the Restfulness in this small way for security.
Preface
I'm developing several web services and a handful of clients (web app, mobile, etc.) which will interface with said services over HTTP(s). My current work item is to design an authentication and authorization solution for the product. I have decided to leverage external identity providers, such as Facebook, Google, Microsoft, Twitter, and the like for authentication.
I'm trying to solve the problem of, "when a request comes to my server, how do I know who the user is and how can I be sure?". More questions below as well...
Requirements
Rely on external identities to indicate who I'm dealing with ('userId' essentially is all I care about).
The system should use token-based authentication (as opposed to cookies for example or basic auth).
I believe this is the right choice for scaling across multiple clients and servers while providing loose coupling.
Workflow
Based on my reading and understanding of token-based authentication, the following is how I imagine the workflow to be. Let's focus for now on Facebook in a web browser. My assumption is that other external identity providers should have similar capabilities, though I have not confirmed just yet.
Note, as of writing, I'm basing the following off of Facebook login version 2.2
Client: Initiates login to Facebook using the JavaScript SDK
Facebook: User authenticates and approves app permissions (to access user's public profile for example)
Facebook: Sends response to client which contains user’s access token, ID, and signed request
Client: Stores user access token in browser session (handled by SDK conveniently)
Client: Makes a request to my web service for a secure resource by sending along the user’s access token in the authorization header + the user’s ID (in custom header potentially)
Server: Reads user access token from request header and initiates verification by sending a request to the debug_token graph API provided by Facebook
Facebook: Responds back to the server with the user access token info (contains appId and userId)
Server: Completes verification of the token by comparing the appId to what is expected (known to itself) and the userId to what was sent on the client request
Server: Responds to the client with the requested resource (assuming the happy authorization path)
I’m imagining steps 5-9 would be repeated for subsequent requests to the server (while the user’s access token is valid – not expired, revoked from FB side, app permissions changed, etc.)
Here's a diagram to help go along with the steps. Please understand this system is not a single page application (SPA). The web services mentioned are API endpoints serving JSON data back to clients essentially; they are not serving HTML/JS/CSS (with the exception of the web client servers).
Questions
First and foremost, are there any glaring gaps / pit falls with the described approach based on my preface and requirements?
Is performing an outbound request to Facebook for verifying the access token (steps 6-8 above) per client request required / recommended?
I know at the very least, I must verify the access token coming from the client request. However, the recommended approach for subsequent verifications after the first is unknown to me. If there are typical patterns, I’m interested in hearing about them. I understand they may be application dependent based on my requirements; however, I just don’t know what to look for yet. I’ll put in the due diligence once I have a basic idea.
For instance, possible thoughts:
Hash the access token + userId pair after first verification is complete and store it in a distributed cache (accessible by all web servers) with expiry equal to access tokens. Upon subsequent requests from the clients, hash the access token + userId pair and check its existence in the cache. If present, then request is authorized. Otherwise, reach out to Facebook graph API to confirm the access token. I’m assuming this strategy might be feasible if I’m using HTTPS (which I will be). However, how does performance compare?
The accepted answer in this StackOverflow question recommends creating a custom access token after the first verification of the Facebook user token is complete. The custom token would then be sent to the client for subsequent requests. I’m wondering if this is more complex than the above solution, however. This would require implementing my own Identity Provider (something I want to avoid because I want to use external identity providers in the first place…). Is there any merit to this suggestion?
Is the signedRequest field present on the response in step #3 above (mentioned here), equivalent to the signed request parameter here in the ‘games canvas login’ flow?
They seem to be hinted as equivalent since the former links to the latter in the documentation. However, I’m surprised the verification strategy mentioned on the games page isn’t mentioned in the ‘manually building a login flow’ page of the web documentation.
If the answer to #3 is ‘Yes’, can the same identity confirmation strategy of decoding the signature and comparing to what is expected to be used on the server-side?
I’m wondering if this can be leveraged instead of making an outbound call to the debug_token graph API (step #6 above) to confirm the access token as recommended here:
Of course, in order to make the comparison on the server-side, the signed request portion would need to be sent along with the request to the server (step #5 above). In addition to feasibility without sacrificing security, I’m wondering how the performance would compare to making the outbound call.
While I’m at it, in what scenario / for what purpose, would you persist a user's access token to a database for example?
I don’t see a scenario where I would need to do this, however, I may be overlooking something. I’m curious was some common scenarios might be to spark some thoughts.
Thanks!
From what you describe I'd suggest to use a server-side login flow as described in
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2
so that the token is already on your server, and doesn't need to be passed from the client. If you're using non-encrypted connections, this could be a security risk (e.g. for man-in-the-middle attacks).
The steps would be:
(1) Logging people in
You need to specify the permission you want to gather from the users in the scope parameter. The request can be triggered just via a normal link:
GET https://www.facebook.com/dialog/oauth?
client_id={app-id}
&redirect_uri={redirect-uri}
&response_type=code
&scope={permission_list}
See
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#login
(2) Confirm the identitity
GET https://graph.facebook.com/oauth/access_token?
client_id={app-id}
&redirect_uri={redirect-uri}
&client_secret={app-secret}
&code={code-parameter}
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#confirm
(3) Inspect the access token
You can inspect the token as you already said in your question via
GET /debug_token?input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
This should only be done server-side, because otherwise you'd make you app access token visible to end users (not a good idea!).
See
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#checktoken
(4) Extending the access token
Once you got the (short-lived) token, you can do a call to extend the token as described in
https://developers.facebook.com/docs/facebook-login/access-tokens#extending
like the following:
GET /oauth/access_token?grant_type=fb_exchange_token
&client_id={app-id}
&client_secret={app-secret}
&fb_exchange_token={short-lived-token}
(5) Storing of access tokens
Concerning the storing of the tokens on the server, FB suggests to do so:
https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.2#token
(6) Handling expired access tokens
As FB doesn't notify you if a token has expired (and if you don't save the expiry date and compare this to the current timestamp before making a call), it's possible that you receive error messages from FB if the token got invalid (after max. 60 days). The error code will be 190:
{
"error": {
"message": "Error validating access token: Session has expired at unix
time SOME_TIME. The current unix time is SOME_TIME.",
"type": "OAuthException",
"code": 190
}
}
See
https://developers.facebook.com/docs/facebook-login/access-tokens#expiredtokens
If the access token becomes invalid, the solution is to have the person log in again, at which point you will be able to make API calls on their behalf once more. The login flow your app uses for new people should determine which method you need to adopt.
I dont' see any glaring gaps / pit falls, but I'm not a security expert.
Once your server has verified the given token (step 8), as you said:
The accepted answer in this StackOverflow question recommends creating a custom access token after the first verification of the Facebook user token is complete. The custom token would then be sent to the client for subsequent requests. I’m wondering if this is more complex than the above solution, however. This would require implementing my own Identity Provider (something I want to avoid because I want to use external identity providers in the first place…). Is there any merit to this suggestion?
IMHO is the way to go. I would use https://jwt.io/ which allows you to encode values (the userId for example) using a secret key.
Then your client attach this token to every request. So you can verify the request without need to a third party (you don't need database queries neither). The nice thing here is there is no need to store the token on your DB.
You can define an expiration date on the token, to force the client authenticate with the third party again when you want.
Let's say you want your server be able to do some action without the client interaction. For example: Open graph stories. In this scenario because you need to publish something in the name of the user you would need the access token stored on your DB.
(I can not help with the 3 and 4 questions, sorry).
Problem with Facebook is that they do not use OpenId connect on top of Oauth (https://blog.runscope.com/posts/understanding-oauth-2-and-openid-connect).
Thus resulting in their custom ways of providing Oauth authentification.
Oauth2 with OpenId connect identity services usually provide issuer endpoint where you can find URL (by appending ".well-known/openid-configuration") for jwk's which can be used to verify that JWT token and its contents were signed by the same identity service. (i.e access token originated from the same service that provided you jwk's)
For example some known openid connect identity providers:
https://accounts.google.com/.well-known/openid-configuration
https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration
(btw it is not a coincidence that Attlasian provides only these two services to perform external login)
Now as you mentioned, you need to support multiple oauth providers and since like Facebook not all providers use same configuration of oauth (they use different JWT attribute names, toke verification methods, etc. (Openid connect tries to unify this process)) i would suggest you to use some middleware identity provider like Oauth0 (service not protocol) or Keycloak. These can be used with external identity providers (Social pages as you mentioned) and also provides you with custom user store.
Advantage is that they unify authentication process under one type (e.g both support openid connect). Whereas when using multiple oauth providers with not unified authentication workflow you will end up with redudant implementations and need for merging different information's under one type (this is basically what mentioned middle-ware identity providers solve for you).
So if you will use only Facebook as identity provider in your app then go for it and make implementation directly for Facebook Oauth workflow. But with multiple identity providers (which is almost always case when creating public services) you should stick with mentioned workaround or find another one (or maybe wait till all social services will support Openid connect, which they probably wont).
There may be hope.. This year, Facebook have announced a "limited login" feature, which, if they were to add to their javascript sdks would certainly make my life easier:
https://developers.facebook.com/blog/post/2021/04/12/announcing-expanded-functionality-limited-login/
At the time of writing, I can only find reference to iOS and Unity SDKs, but it does seem to return a normal JWT, which is all I want!
I am currently working on a website built with Backbone.js. The site has a RESTful API built in Symfony with FOSRestBundle. Developing was going fine, until I stumbled in to some user-related tickets.
From what I understand, the best way to handle this type of problem is with a token based system, where the user gets an access token after an approved login. I will describe my current perception of the workflow, and ask questions along the way. More importantly, please correct me if I have misunderstood.
First, the user the accesses the login form, then the user types in credentials, and an AJAX request is send to the server. From what I understand this should all be handled with SSL, but with Backbonejs, you can't simply say that the login page should be accessed with HTTPS, as Backbone is a one-page framework. So will this force me to use HTTPS through out the application?
In the next step, the REST server validates the credentials, and they are approved, then the REST server sends an access token to the client. Is this token saved (on the client-side) in local storage or a cookie?
Also is the login stored at the server, so that the REST server can log the user out after a certain amount of time?
Now, the client sends this access token along with other request, so that the server can identify the client, and approve the request or not. So the access token is also stored on the REST server?
Lastly is this what the smart people call "oauth", or does it relate to it?
Thank you.
Let's take your questions one at a time.
From what I understand this should all be handled with SSL, but with Backbonejs, you can't
simply say that the login page should be accessed with HTTPS, as Backbone is a one-page
framework. So will this force me to use HTTPS through out the application?
Ok, there's a lot to unpack there. Let's start with SSL/HTTPS. HTTPS is a protocol; in other words it defines how you send packets to/from the server. It has nothing whatsoever to do with whether your application is single or multi-page; either type of site can use either HTTP or HTTPS.
Now, that being said, sending login info (or anything else containing passwords) over HTTP is a very bad idea, because it makes it very easy for "bad people" to steal your users' passwords. Thus, whether you're doing a single-page or a multi-page app, you should always use HTTPS when you are sending login info. Since it's a pain to have to support both HTTP and HTTPS, and since other, non-login data can be sensitive too, many people choose to just do all of their requests through HTTPS (but you don't have to).
So, to answer your actual question, Backbone isn't forcing you to use HTTPS for your login at all; protecting your users' passwords is forcing you.
In the next step, the REST server validates the credentials, and they are approved, then
the REST server sends an access token to the client. Is this token saved (on the
client-side) in local storage or a cookie?
While any given framework might do it differently, the vast majority use cookies to save the token locally. For a variety of reasons, they're the best tool for that sort of thing.
Also is the login stored at the server, so that the REST server can log the user out
after a certain amount of time?
You've got the basic right idea, but the server doesn't exactly store the login ... it's more like the server logs the user in and creates a "session". It gives that session an ID, and then whenever the user makes a new request that session ID comes with the request (because that's how cookies work). The server is then able to say "oh this is Bob's session" and serve the appropriate content for Bob.
Now, the client sends this access token along with other request, so that the server can
identify the client, and approve the request or not. So the access token is also stored
on the REST server?
If you're running two separate servers they're not going to magically communicate; you have to make them talk to each other. For this reason your life will be easier if you can just have one (probably REST-ful) server for your whole app. If you can't, then your REST server is going to have to ask your other server "hey tell me about session SESSION ID" every time it gets a request.
Lastly is this what the smart people call "oauth", or does it relate to it?
Kind of, sort of, not really. OAuth is an authorization standard, so it's sort of tangentially related, but unless your login system involves a whole separate server you have no reason to use it. You could use OAuth to solve your "two servers, one REST-ful one not" problem, but that would probably be overkill (and regardless it's outside the scope of what I can explain in this one Stack Overflow post).
Hope that helps.