I have
UI (a single page app)
an external authentication server
my own authorization server
resource server (my own backend APIs)
Here's what I am trying to do
UI/User gets an AuthN token from the external authentication server.
UI sends the AuthN token to get the an AuthZ token from my own authorization server
UI uses the AuthZ token to retrieve data from the resource server
But the problem is I don't know if the user is still authenticated anymore because I stopped using the AuthN token from step 3. Should I use both tokens together? or somehow consolidate the 2 tokens into one? Hope to get some ideas from here. Thanks!!
COMPONENTS
This is the standard way of managing components:
UI makes an OpenID Connect redirect to the Authorization Server (AS)
AS makes a second OpenID Connect redirect to the authentication system. There could be more than one of these, eg Google, Facebook.
After user sign in the AS issues the same tokens for your UI and resource server, regardless of how the user signs in. The UI sends access tokens to the resource server which can authorize based on scopes and claims received.
Unless you have special reasons, do not use foreign tokens from authentication systems in your own applications. This is because you are not in a position to control their contents.
OPENID CONNECT RE-AUTHENTICATION MECHANISMS
The OpenID Connect prompt and max-age parameters can be used to control how frequently the user is prompted to re-authenticate, and the auth_time claim can be issued in ID tokens to inform the UI of the last authentication time.
For example your app could use access tokens that last 15 minutes. Whenever they expire you could send a request with a prompt-none parameter to see if the user is still authenticated. If not then you will receive a login_required response and you could then redirect the user to re-authenticate.
SINGLE LOGOUT
Knowing if the user is still authenticated suggests you need to know if they signed out in another app. OpenID Connect has four Single Logout Mechanisms that you should be aware of, and which may possibly work for your scenario.
This is a technical area that has never worked perfectly in any Single Sign On technology though. This may be because you do not control all apps, or because of technical limitations, eg Google may not inform the Authorization Server if the user signs out of Gmail.
SUMMARY
Your apps should only use the authorization server tokens. Use OIDC request parameters to control when the user must re-authenticate.
I try to integrate my Python app(backend client) into the Keycloak system using openid connect, why do I have to fill the Valid Redirect URIs field? From my understanding for OAuth 2.0, the backend channel just need to exchange the code for id and access tokens in the Authorization Code Flow process
Why do we need a redirect URL here, shouldn't it only filled in the front end client?
Any Ideas?
We need it to restrict to what URL we are allowed to redirect to after a successful login. We do this to improve the security for your users, because it would be a big security problem if the provider could redirect to any URL after login.
The client which receives the authorization code and the one which exchanges the code for tokens must be the same client - at least from the Authorization Server's point of view. Theoretically you can have separate applications which handle parts of the OAuth flow, but they must be using the same client ID.
Ideally, the redirect URI points to an endpoint exposed by your Python backend. After Keycloak (or any Authorization Server) finishes authentication and authorization, it will redirect the user to that endpoint together with the authorization code. This way the authorization code will go straight to the backend client, the frontend app will never have to handle it. By registering the URL you want to make sure that no one performs authorization using your client ID, but asks to send the code to another application.
I was reading the site Oauth.com trying to understand how implement security in a Single-Page App when i found this statement:
"The only way the authorization code grant with no client secret can be secure is by using the “state” parameter and restricting the redirect URL to trusted clients. Since the secret is not used, there is no way to verify the identity of the client other than by using a registered redirect URL."
If i undestanf correctly, he says that i can verify the identity of my SPA using a registered redirect URL.
Question 1: If I redirect the authorization code to a url (web server), how can i get it (or the access token, or the protected resources) back in my SPA that is running in a browser ?
Question 2: what kind of check can be done in this registered url to verify my SPA identity?
Q1. You receive an authorization code as the login response in a query parameter. Then swap it for an access token. Then call the API with the access token.
Q2. Use HTTPS to prove that you own the redirect URI. Use PKCE to create a one time use secret at runtime.
RESOURCES OF MINE
Maybe these resources will give you a clearer idea - feel free to post any follow up questions ..
SPA Message Workflow
SPA Tutorial + Code Sample
The authorization code is included in the URL as the code query parameter, so you can access it from the SPA (e.g. with window.location.search). It will depend on the OAuth2 provider whether you will be able to exchange that code for an access token from your SPA.
If you use a web server to do the code->token exchange, you'll need to put the tokens in a database that your SPA has access to.
The quote from OAuth.com mentions two security measures, the state parameter, and registered redirect URLs. These prevent two separate attacks:
The state parameter prevents an attacker from using your URL with a stolen authorization code to retrieve an access token for a victim user and associate it with the attacker's account. By verifying that the state parameter in the URL is the same state that you provided to the authorization server, you can be sure that the redirect was generated by the authorization server, and not an attacker.
The registered redirect URL prevents an attacker from using your client ID, but sending users to an authorization URL that, after authorization, will redirect them to the attacker's web server instead of yours, allowing the attacker to retrieve the user's access token. With a registered redirect URL, the authorization server will only redirect to the pre-registered redirect URLs, which you control, and will not redirect to an attacker's web server.
An alternative to implementing this flow yourself, particularly for a SPA, which has limitations in what OAuth grant types are available to it, is to use a managed OAuth service. Xkit, which I work on, is designed to work with SPAs and moves all of the OAuth flow (including security considerations) out of your code.
I have developed a stateless API on a server at api.com. Some API endpoints require authentication.
I have a website on a separate server at website.com. When a user authenticates with the website, the website server needs retrieve some data from an API endpoint which requires authentication (/tweets, for example). This data will be used in the server response (to render the tweets, for example).
The server response will also download some JavaScript in the browser that will subsequently need to retrieve (via XMLHttpRequests (XHR)) some data from an API endpoint which requires authentication (/tweets, for example).
This architecture represents an isomorphic web application. The server renders the whole page when requested, and thereafter the client handles user actions using JavaScript.
--
At a very basic level, I could use HTTP Basic Authentication for both website.com and api.com. However, the browser would prompt the user to enter their credentials when they first login to website.com, and repeatedly when the client makes an XHR to a endpoint requiring authentication.
I want the user to login with their credentials once at website.com. This is similar to the current Twitter website. Once you login to twitter.com, the website server identifies you as authenticated and responds with a HTML page containing JavaScript downloads. The JavaScript app then (presumably) makes authenticated XHRs to the stateless Twitter API.
The API is a separate server by design. Eventually the API could be opened up for third parties, although this is not an initial requirement.
How can I achieve this? I'm looking for:
the simplest secure solution
a solution that uses OAuth (if applicable)
Both would be great!
The situation you describe is exactly what OAuth is designed for: a client authorizes with one server and then obtains access to resources on another server. In your case, website.com is the authorization server and api.com is the resource server. In a nutshell, the authorization server sends an access token to the client, which the client can then pass on to the resource server to prove that they have permission to access the resource. In order for this to work, the resource server (api.com) needs to either check back with the authorization server (website.com) to verify that the token is valid or be informed about the token in advance. So there is a triangle of communication between the client, the authorization server and the resource server in which a shared secret is passed around. Because of this, it is absolutely necessary to use secure connections (HTTPS) in all parts of the chain; otherwise, someone could intercept the token and pretend to be the authorized client. This is kept within reasonable bounds by using limited-access tokens which do not fully authenticate the user, but it is nonetheless a problem that you should try to prevent.
While theoretically secure, OAuth is a complicated system and it is hard to get right. Some people think it is practically impossible to get right (notably Eran Hammer, the original lead author of the OAuth 2.0 specification who decided to withdraw from the working group). However, given that you need to use HTTPS anyway, you could avoid OAuth altogether and instead use a little-known builtin feature of HTTPS (or actually, TLS) called (surprise!) client authentication.
As you probably already know, in the HTTPS protocol, the server (website.com) uses a certificate signed by a trusted authority to authenticate itself. This is a well understood and very secure mechanism (at least by internet standards), provided that the certificate is uncompromised and that the latest version of TLS is used. The client can do the same, i.e. authenticate with a certificate that was signed by a trusted authority. The latter authority can be the server (website.com) for this purpose, because the server can trust itself. So the elegance of TLS client authentication is that the client does not need to contact a third party in order to obtain a certificate; the client and the server can cooperate to provide the client with a certificate that the server can trust. This is potentially even very user-friendly, because the client certificate needs to be transferred and installed only once and can then be used for authentication on subsequent sessions, possibly without the user even needing to enter a password. The same client certificate can also be used for HTTPS connections with other servers (e.g. api.com), provided that those servers know about the certificate and trust the authority that signed it (website.com).
TLS client authentication is likely to be more secure than OAuth, while it might require less interaction from the user overall (depending on the way in which the client certificate is handled in the browser). On the other hand, most users are probably unfamiliar with the particular mechanics of TLS client authentication. If users need to log in from many different devices or need to authenticate to many different servers, this workflow may be confusing or cumbersome because each device needs to have a certificate and the certificate may have to be selected manually by the user when a new server is visited for the first time.
To summarize:
In both cases, website.com provides the client with a means to authorize for access to api.com, which api.com needs to know about. So api.com cannot be 100% stateless; it needs to have some knowledge about the means of authorization that website.com communicated with the client.
Both cases require a secure connection (HTTPS).
In OAuth, the means to authorization is a "shared secret" limited access token (also known as "pseudoauthentication"), while in TLS client authentication, it is a private certificate that fully authenticates the client because it was signed by a trusted authority.
In OAuth, authorization is done on the data layer (applications explicitly communicate the access token) while in TLS client authentication, authentication is done on the transport layer (meaning that your API does not actually need to be aware of authentication or even authorization, if the webserver is configured to allow certain endpoints only to authenticated clients).
TLS client authentication is probably more trustworthy, but OAuth is probably more familiar to users because it works with password logins "as usual".
Some clarifications in response to the comments:
How does website.com know the user is logged in? How does website.com remember the user is logged in (i.e. between browser refreshes)?
By storing the access token in a secure cookie on the client side. On every request from the client to website.com, the access token is included in the request headers. This way, website.com can be assured that every request is either authenticated (if the request contains the access token, i.e. the user is logged in), or the visitor is a guest.
How does the browser make authenticated XHR requests?
By sending the access token in the request header, just like for website.com. Obviously, this requires the cookie to be accessible to the client.
website.com needs to authenticate with api.com when creating the server response
When it does that, it just sends a (HTTPS) request on the user's behalf. It's the same thing where the access token is included in the request headers. website.com always has the access token of the user when it does this, because it either is about to provide it to the user or it just received it from the user.
Further information on Wikipedia:
https://en.wikipedia.org/wiki/Oauth
https://en.wikipedia.org/wiki/HTTP_Secure#Use_as_access_control
https://en.wikipedia.org/wiki/Transport%5FLayer%5FSecurity#Client-authenticated%5FTLS%5Fhandshake
As Julian mentioned OAuth is complicated and hard to get right. I would find a trusted opensource project and use that as your Identity Server.
Also, instead of OAuth, I would look into OpenID Connect. It is a relatively new protocol (Jan 2014), but has been getting a lot of attention. Google+ for example is using it. It combines the authorization framework of OAuth and adds the identity and authentication framework on top. OAuth was never truly designed for that, which is one of the reasons why Eran left the project. This new protocol is the future of Single Sign On and will replace WS-Federation and SAML. http://openid.net/connect/
Here are all the current libraries available: http://openid.net/developers/libraries/
Again, if you're using C#/.NET, here is their project currently in Beta 3 (should be live in Januaray) that provides every possible configurable scenario with examples. If nothing else, it gives you the code to see how you can implement it. https://github.com/thinktecture/Thinktecture.IdentityServer.v3.Samples
See this talk for more details: http://vimeo.com/97344501
Hopefully this gives you some food for thought.
I think something like this can be done
1) User logs in at website.com, website.com will create a temporary token T for future API usage
2) Whenever some data is required from api.com, website will request that data and send token T in the request api.com/getdata/params=...&token=T
This request is better done with SSL to protect the token.
Please also check http://en.wikipedia.org/wiki/Cross-origin_resource_sharing - not all browsers will let you request data from another domain from Javascript.
3) When api.com receives such a request, it will make a separate and secret connection to website.com, something like website.com/checktoken/?token=T and get all necessary information about the user at website.com to send him relevant data
4) User gets all information, not leaving website.com and not having to authenticate at two places
Basically, you need to decide whether you want to authorize website.com domain to use api.com methods, or to authorize users of website.com to use api.com. From your description I understand that your are talking about the second case.
Then some kind of OAuth(OAuth2.0) implementation could be suitable for you with(probably) a single authentication point for the all your sites: it could be the passport.com. When user wants to use api.com or website.com or any other your site, he will be redirected to passport.com and required to authenticate there. After authentication on passport.com, user will be redirected back and provided with authorization code, that is used to request token, that, in turn, could be used by api.com and website.com to get information about the user(roles, permissions etc). On website.com you could use this token to access api.com, because api.com could use this token to validate user against passport.com. Also you could use this token to request info from api.com by AJAX(you need to overcome the Same Origin Policy problem, though, but it's feasible).
UPDATE:
The idea is that user needs to be authenticated on passport.com to use website.com & api.com.
From the OAuth2.0 standpoint you could say that user authorizes website.com & api.com to use his info on passport.com.
So authentication cookie exists only for passport.com domain and
is not sent anywhere else(to website.com or api.com). This is according to the Same Origin Policy.
So more detailed description of OAuth2.0 implementation in your case would be:
User wants(or have to) to be authenticated on website.com. Request to the passport.com is performed(with specified REDIRECT_URL on website.com where to return later): passport.com/auth/?redirectTo=REDIRECT_URL
If user is not authenticated on passport.com(there is no Auth cookie there) then login page of passport.com is displayed to him. When authenticated, new AUTHORIZATION_CODE is saved on passport.com(in database) for that user.
After authentication on passport.com(or if user already was authenticated there) passport.com redirects user back to REDIRECT_URL with AUTHORIZATION_CODE in querystring: {REDIRECT_URL}?code=AUTHORIZATION_CODE
Website.com uses this AUTHORIZATION_CODE to request ACCESS_TOKEN from passport.com:
passport.com/token/?code=AUTHORIZATION_CODE
Having ACCESS_TOKEN, website.com could use it to request information about user from passport.com. Plus you could pass this ACCESS_TOKEN to api.com when requesting something from api.com. api.com could check user's identity against passport.com to check if he has enough permissions. Is it safe to pass ACCESS_TOKEN to api.com? You need to provide some sort of key to api.com anyway(if api.com is not a public api), so using this approach at least ACCESS_TOKEN is not a static one: it has lifetime, and it's user-based.
Again, it's very simplified OAuth2.0 example without secret keys, scopes, access grants and few other details.
Here is a simple way. It adds some overhead/latency, but at least it works and is dead simple:
let website.com act as a proxy and forward all calls to api.com
browser <-> https://website.com/api/url <-> https://api.com/url
You can just reuse the creditentials to make a separate session from website.com to api.com
My API should support "official" apps as well as third-party apps. For official apps I don't want the user to explicitly grant access (after login).
I'm I right, that I can skip the "allow" screen as long as I restrict the redirect_uri to something like http://official.service.tdl? Or does this has security implication I didn't think of?
For official apps, assuming they are confidential clients, you can use client credentials grant. It sounds like the official app and your API will have a pre-established relationship, and your organization also controls the access server. In the client credentials grant the client can just talk to the access server directly and gets a token to use with your API.
If you want to bypass user authorization when the redirect uri is from your company, then you should look at Authorization Code Redirection URI Manipulation, in particular
In order to prevent such an attack, the authorization server MUST
ensure that the redirection URI used to obtain the authorization code
is identical to the redirection URI provided when exchanging the
authorization code for an access token. The authorization server
MUST require public clients and SHOULD require confidential clients
to register their redirection URIs. If a redirection URI is provided
in the request, the authorization server MUST validate it against the
registered value.
and you would need to make sure you don't have any open redirects that match your redirect URI.
Or you can just let users authorize access once and use refresh tokens so they don't need to re-authorize access.
Since the redirect URI is coming from the client browser, you cannot guarantee that someone won't setup their browser to change the URI to make it look like it's coming from your official URI. The question then becomes, what security issue would you run into if a third party app bypasses the explicit access.