Can I redirect with access_token without an "allow" screen? - authentication

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.

Related

How do I allow users to link with Spotify without exposing my Spotify client id & secret on the frontend?

Here's my setup. I have a NodeJS express server providing endpoints and hosting my frontend which I've built using React. I have the Spotify client id & secret stored in a .env file which the frontend references, and therefore publicly exposes.
Currently, for users to link with Spotify, the frontend can redirect to Spotify's authorize page and pass the client id and secret (and a redirect uri) in the url params. They then log in with their Spotify credentials and accept my app's terms. Then Spotify then redirects them to the provided redirect uri, which is just another page of my React app. The user's refresh token is passed as a url param for my frontend to receive. It then sends refresh token to my server using one of my endpoints and I store it in my database under their account.
This works fine, except for the fact that my app's client id and secret are publicly exposed through my frontend. I'm trying to work out a way to allow users to link with Spotify without having the frontend know this information, because if it leaks then people can make calls to Spotify's API on my behalf. But I can't seem to get around the fact that the client's browser needs to at some point have access to something like this.
const url =
'https://accounts.spotify.com/authorize?' +
querystring.stringify({
response_type: 'code',
client_id: spotify_client_id,
scope: spotify_scope,
redirect_uri: spotify_redirect_uri
})
window.location.href = url
I'm new to web development so there may be something obvious I'm neglecting. If anyone has any ideas, I'm all ears. Thanks in advance!
In this particular scenario, you’ve designed around the entirely wrong OAuth flow for the job. Client credentials-style authentication/authorization is not intended to be used in the manner you describe, for the reasons you describe. Instead, you should be using the offered authorization code with PKCE flow, which provides similar functionality for web apps, etc. without necessitating the exposure of your sensitive authentication secrets.
Spotify is pretty explicit about this in their documentation (emphasis mine):
Which OAuth flow should I use?
Choosing one flow over the rest depends on the application you are
building:
If you are developing a long-running application (e.g. web app running on the server) in which the user grants permission only once,
and the client secret can be safely stored, then the authorization
code flow is the recommended choice.
In scenarios where storing the client secret is not safe (e.g. desktop, mobile apps or JavaScript web apps running in the browser),
you can use the authorization code with PKCE, as it provides
protection against attacks where the authorization code may be
intercepted.
For some applications running on the backend, such as CLIs or daemons, the system authenticates and authorizes the app rather than a
user. For these scenarios, Client credentials is the typical choice.
This flow does not include user authorization, so only endpoints that
do not request user information (e.g. user profile data) can be
accessed.
The implicit grant has some important downsides: it returns the token
in the URL instead of a trusted channel, and does not support refresh
token. Thus, we don’t recommend using this flow.
It may go without saying, but since you’ve already elected to publicly publish your app secret, you should consider it compromised and invalidate it immediately before malicious actors are able to indeed use it to craft abusive API requests.

keycloak, why does backend channels need a redirect url

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.

Oauth2 Single-Page Apps Security Considerations

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.

Single sign-on flow using JWT for cross domain authentication

There is a lot of information on the web about using JWT (Json Web Token) for authentication. But I still didn't find a clear explanation of what the flow should be when using JWT tokens for a single sign-on solution in a multiple domains environment.
I work for a company which has a lot of sites on different hosts. Let's use example1.com and example2.com. We need a single sign-on solution, which means if a user authenticates on example1.com, we want him to also be authenticated on example2.com, automatically.
Using the OpenId Connect flow, I understand that the user who wants to authenticate on example1.com will first be redirected to the authentication server (or OP : "OpenId Provider"). The user authenticates on that server which then redirects him back to the original example1.com site with a signed JWT token. (I understand there is another flow which returns an intermediate token that itself can be exchanged for the real JWT token later on, but I don't think this is required for us)...
So now the user is back on example1.com and is authenticated! He can make requests, passing the JWT token in a Authentication header and the server is able to verify the signed JWT and therefore is able to identify the user. Nice!
First question :
How should the JWT token be stored on the client? There is, again, a lot of information about this, and people seem to agree that using Web Storage is the way to go rather than good old cookies. We want the JWT to be persistent between browser restarts so let's use Local Storage, not Session Storage...
Now the user can restart his browser and he will still be authenticated on example1.com, as long as the JWT token is not expired!
Also, if example1.com needs to make an Ajax request to another of our domains, I understand configuring CORS would allow that. But our main use case is not cross-domain requests, it's having a single sign-on solution!
Therefore, the main question :
Now, what should the flow be, if the user goes to example2.com and we want him to be authenticated, using the JWT token he already has? Local Storage doesn't seem to allow cross-domain access so at this point the browser can't read the JWT token to make requests to example2.com!
Should :
The user be redirected to the authentication server again? When the user authenticated for example1.com, the
authentication server may have set a cookie on the user so this new authentication request for example2.com could use that cookie to see that the user is already authenticated and immediately redirects him back to
example2.com with the same JWT token?
Or can the browser, on example2.com, access the JWT token without having to go to the authentication server again? I see there are cross-storage solutions, but are those widely used? Are they the suggested solution to a cross domain SSO environment?
We don't want anything fancy, we would be happy with the mostly used solution!
Redirecting the user to the central authentication service when the user is not logged in to request credentials and issue a new authentication token is the common scenario in Single Sign On systems using well-known protocols like oauth2 or OpenId Connect
However when this schema is used across domains the main drawback is that the user is going to be redirected and authenticated each time he navigates to other domain due to same-origin policy: the access token can not be shared between domains (example2.com can not access data of example1.com), so the target domain will treat user as unauthenticated, redirecting him to the central SSO service.
To prevent the authentication service from re-requesting credentials, it is common to have a session cookie (not an access token), but there is a tecnique to share data across domains using browser localStorage/cookies and a iframe pointing to an intermediate domain sso.example.com
To authenticate the user in example1.com, redirect him to the authentication server in sso.example.com, issue a JWT after authenticating and store it in the localStorage of this domain. After this, redirect user to the origin domain example1.com
Create an iframe in example2.com pointing to sso.example.com. The iframe in sso.example.com reads the JWT token and sends a message to the parent page
The parent page receives the message and gets the attached token continuing with the SSO flow
There is no problem with same-origin policy because sso.example.com has access to its localStorage and the communication between iframe and the parent page is allowed if origin and target domains recognize each other (see http://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage)
To simplify development, we have released recently a cross domain SSO with JWT at https://github.com/Aralink/ssojwt
This method is perfectly compatible with SSO flows. It is just a way to share the authentication token without redirections and avoid unnecessary log-ins when the domains are federated
The user should be redirected to the authentication server again and get a new token (JWT), one that is specifically targeted for example2.com. This is how OpenID Connect and any other cross-domain federated SSO protocol works.
Not sure if this answers you question, but if your main goal is single sign-on, I think a simple reverse proxy would solve your problem (at least the cross-domain storage one).
So
example1.com
example2.com
would become something like
example.com/example1
example.com/example2
(And from a user side, this is usually cleaner)
If that is not an option, you might have to set up so that when a user authenticates in 1 domain, it uses AJAX/hidden iframes to create an authentication with the other domains as well (sending a 1 time token via url if you must).
and if THAT'S not an option, you might have to resort to username+pin, as browsers are getting stricter about cross-domain interaction.

Authentication for isomorphic web app with separate website and API servers

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