OAuth2 without Client Secret – Possible Phishing? - javascript

I've been reading the OAuth2 specs over and over, but I can't figure out one thing. Isn't the Authorization Code flow without Client Secret (which is now recommended for single page apps) highly insecure because it can easily be used for phishing? Let me explain:
The Client redirects the Resource Owner to the Authorization Server, passing the Redirect URL and Client ID.
The Resource Owner approves the request and the Authorization Server redirects him to the given Redirect URL and passes the Authorization Code.
Now, in reality, the Client that requested the authorization is a phishing site which the user, unfortunately, didn't recognize. The Redirect URL passed to the Authorization Server points to the malicious Client, not to the legitimate one. The Client ID is a public information, so setting up such site is fairly easy.
What will happen if the Client Secret is required?
The malicious Client will receive the Authorization Code, but it doesn't know the legitimate Client Secret.
The Resource Server will refuse to send an Access Token, as a valid Client Secret wasn't provided. The user information is safe.
But what if the Resource Server doesn't require the Client Secret?
The malicious Client will receive the Authorization Code, and even though it doesn't know the Client Secret, it will request an Access Token.
The Resource Server will accept the request, as a valid Authorization Code and Client ID is provided and Client Secret is not required. The malicious Client obtains the Access Token and the user information is compromised.
Am I missing something or is this correct and there's nothing that can be done to make using OAuth2 with single page apps more secure?

The resource server doesn't require a client_secret as only valid clients can obtain an redeem an authorization code.
A client must be validated against not only the client_id but also the redirect_uri that is registered to the client. When registering an OAuth Client you should require a list of permitted redirect_uri's that are permitted for use with the client_id.
So if a malicious client made a request it would fail validation as you must only redirect if the redirect_uri is permitted.
This is detailed in the OAuth 2.0 RFC under section 3.1.2.2 https://www.rfc-editor.org/rfc/rfc6749#section-3.1.2.2

Related

Is having the client id and client secret in code a security risk?

I'm using google calendar API to add the event to calendar. I was wondering if it would be security issue as I'm using client Id and API code in JS which can be exposed to someone using the application?
Also, if it is the case how to secure those keys?
PS- Docs that I'm following- https://developers.google.com/calendar/quickstart/js
Have you noticed the redirect uri? THe redirect uri tells googles auth server where to return the access token.
Even if I grab your client id and client secret. I cant use it because the server is going to send the access token to the redirect uri to your server.
This is why you should not set localhost as a redirect uri. 😉
RFC oauth2 redirection endpoint
After completing its interaction with the resource owner, the
authorization server directs the resource owner's user-agent back to
the client. The authorization server redirects the user-agent to the
client's redirection endpoint previously established with the
authorization server during the client registration process or when
making the authorization request.
That being said you should try to keep these keys private you should not share them or add them to open source projects. However Client sided applications like javascript is a gray area.

Simplest way to get current user logged in Keycloak

I have implemented a really simple keycloak integration on my maven java web app.
Assuming I am calling a url directly for the keycloak log in page .
http://localhost:8180/auth/realms/myrealm/protocol/openid-connect/auth?client_id=myclientid&response_type=code&scope=openid&redirect_uri=http//localhost:8080/mypage.html
After entering my username & password on success i am being redirected on mypage.html , the url is like this
http://localhost:8080/mypage.html?session_state=c9482da3-50ff-4176-bf3c-54227271c661&code=5d4aebda-54d8-41ad-8205-c4d7e021770f.c9482da3-50ff-4176-bf3c-54227271c661.d5c1b6ac-c427-46da-8509-f2689849103b
If I break this down its
http://localhost:8080/mypage.html?
session_state=c9482da3-50ff-4176-bf3c-54227271c661&
code=5d4aebda-54d8-41ad-8205-c4d7e021770f.c9482da3-50ff-4176-bf3c-54227271c661.d5c1b6ac-c427-46da-8509-f2689849103b
What would be the simplest - easiest way to get the user currently logged so i can display it's name ?
Looking at the requests you have made you have not completed the OIDC code flow.
I'm assuming that your java application is acting as the OIDC client, in which case it will need to exchange the authorization code for access, id and refresh tokens by calling the token endpoint of your realm.
e.g.
POST /auth/realms/mmyrealm/protocol/openid-connect/token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
A description of the Token Request
The simplest way would be to use a Java OIDC Client or OAuth2 client to do the authorisation and cod exchange for you and provide OAuth2/OIDC token primitives for you to code against.
Have a look at:
Scribe Java OAuth2 client
Nimbus OIDC SDK
The details of the user will be in claims within the tokens returned by the token endpoint, if you are including the user claims in your tokens.
Edit:
The OIDC Authorization code flow is one of the OIDC authorisation flows. It provides the benefit of not exposing any of the actual tokens to the user agent - e.g. web browser - and allows the oidc client to authenticate with the token server before exchanging the code for the OIDC tokens
At a high level the following occurs:
OIDC Client makes an authentication request
Client authenticates - this could be an end user
Authorisation server returns an Authorisation code - on a redirect - to the client
OIDC Client retrieves Access, ID and Refresh Tokens from the authorisation server's token endpoint
If needed User info is retrieved from the UserInfo endpoint or thge access token is inspected using the introspect endpoint
Details of the actual user will be in claims with in the ID token, which is a plain JWT.
Keycloak allows you to embed the claims in the Access token too.
After authentication with Keycloak you will be redirected back to your web applications redirect URI.
As per your breakdown
http://localhost:8080/mypage.html?
session_state=c9482da3-50ff-4176-bf3c-54227271c661&
code=5d4aebda-54d8-41ad-8205-c4d7e021770f.c9482da3-50ff-4176-bf3c-54227271c661.d5c1b6ac-c427-46da-8509-f2689849103b
Your requst handler will need to extract the code from that request and then make another call to keycloak to exchange the authorisation code for Access, ID and refresh tokens
e.g.
POST /auth/realms/myrealm/protocol/openid-connect/token HTTP/1.1
Host: localhost:8180
ContentType: application/x-www-form-urlencoded
Authorization: <whatever method your oidc client is usingL
grant_type=authorization_code&
code=5d4aebda-54d8-41ad-8205-c4d7e021770f.c9482da3-50ff-4176-bf3c-54227271c661.d5c1b6ac-c427-46da-8509-f2689849103b&
client_id=myclientid&
redirect_uri=....
Ideally you have a route handler for accepting the tokens - maybe a tokens enpoint that also accepts query parameters that indicate the original uri requested so that you can redirect back to that if this is a user facing web application.
If it is completely programatic then you can achive all of it using the nimbus sdk.
The has a good summary of the various parts of Authorization Code flow
https://rograce.github.io/openid-connect-documentation/explore_auth_code_flow

Understanding JWT

I've spent a couple weeks trying to wrap my head around JWT objects. The premise makes sense but where I get confused is the security aspect. If I am a Javascript Client (e.g. Firebase) and want to send a secure request to an api using Open Auth, I would encrypt my message with a key. However, since the client source may be viewed how can I secure my Key so malicious requests don't go through. Am I missing something. Is there a way to secure the key?
Joel, I think you got the directions wrong ;)
One would use JWT within the OAuth protocol to achieve what some people might call "Stateless Authentication", meaning that the auth server would issue a signed token (for e.g. a client application or a user) after successful authentication (of the client or user) without storing info about/ of it, which would be required when using opaque token.
The signed token could be used by your JS client to e.g. call a certain REST-API endpoint (on a so-called resource server) that would verify the signature of the token and authorize your request or not, based on the content (the claims) of the JWT.
Both, your client application as well as the resource server are able to introspect the token and verify its signature because they either have a shared secret with the auth server (who used the secret to sign the token in the first place) or know the public key that corresponds to the private key the auth server used to sign the token (as Florent mentioned in his comment).
JWTs can also be encrypted, which is useful if the resource server or the auth server require sensitive information but don't want to store/ access the data. You would not be able to introspect it as long as you don't have the used encryption secret.
... long story short, the OAuth protocol describes client auth against a resource or an auth server. JWT can be used to transfer auth prove (as a Bearer token within the Authorization header). However, the idea of using JWT in the OAuth flow is not to "send a secure request to an api".
The encryption process is performed using the public key of the recipient.
Your client has no private key to generate and manage.
If you want to receive and decrypt such JWT, then your client has to create a key pair (private and public) for the session only and then exchange the public key with the server.
When building an api server, I prefer the client do the encryption process on their own server, and send the encrypted data after that. Everything is under https.
If the encryption somehow must be done on the web client side, I prefer the key to be very short-lived & time based, and both the api server and client have the agreed special algorithm to generate that key again. Therefore, if the key is hacked somehow, the attacker can not benefit in long term.

Client application and token authentication/validation OAuth

I am trying to implement OAuth with my javascript client application.
I already have the server up and running.
My workflow is as following:
Open app
Check if token is present
Validate the token
If not present or not valid go to oauth server for the token
Get back to app and repeat 2 and 3
I everything is ok show my app
I am not sure how to implement point 2. I understand that I need to make another call to the server for this but where is the validation endpoint?
I only have /authorize, /user and /logout
Also how should the request be formed? Do I attach token as a GET parameter and thats all?
What if somebody intercepts valid the token?
Depends on your application, but since you have a javascript web application, that most probably can't keep it's credentials secret, you'll need to implement the Implicit Grant as stated in the OAuth 2.0 spec. If however your application CAN keep it's credentials secret, you should implement the Client Credentials Grant (server side application) because it's more secure.
I am not sure how to implement point 2
You should store your access token somewhere in your web application, for instance in localStorage.
The first time a user will access your web application, the user will have NO access token and NO session with your authorization server. Your web application could see if you have an access token by doing something like:
if (!localStorage.getItem('accessToken') {
window.location.replace('https://your-authorization-server/authorize?response_type=token&client_id=<CLIENT_ID>&redirect_uri=<CALLBACK_URL>&scope=<SCOPE>');
}
If there's no access token, you'll need to redirect to your authorization server so that the user can log in with the authorization server. After the user has logged in, the authorization server will redirect the user back to your web application (after the user has granted permission to whatever resource your web application likes to access on behalf of said user) with a valid access token. This access token must be exposed as a hash fragment named access_token, i.e:
https://web-app.com#access_token=123456789X
Now your web application can extract the access token and store it somewhere.
I understand that I need to make another call to the server for this
but where is the validation endpoint? I only have /authorize, /user
and /logout
Your authorization server requires a token validation endpoint. This endpoint will be used by your web application to validate said token (the one you stored somewhere, for instance localStorage). When this endpoint is called with an access token and it turns out the token is valid, the user can continue, otherwise the user will be redirected to the authorization server. If the user already has a session with the authorization server, the user will be redirected back immediately with a new access token, otherwise the user needs to authenticate (login) first.
Also how should the request be formed? Do I attach token as a GET
parameter and thats all?
Some send a GET request with the access token as a url query parameter, others send a POST request with the access token as the payload of the request body.
What if somebody intercepts the valid token?
Always use https and your access token should be valid for a limited amount of time.
Something to keep in mind is that because your application can't keep it's credentials secret, you need to Implement the Implicit Grant, this means you immediately receive the access token when the authorization server authorized the web application based on Client Id and domain. As opposed to Client Credentials Grant where you first receive an Authorization Code that needs to be exchanged for an access token. When using Implicit Grant you can NOT use Refresh Tokens. This means the user NEEDS to go through the entire flow with the authorization server to obtain a new access token. But this isn't really a big deal, because the user will already be logged in with the authorization server, resulting in an immediate redirect, so when implemented correctly in the web application, the user won't notice.
This is just covering it in broad strokes, it really helped me (and I advise you) to read the OAuth 2.0 spec. Hope this helps you out a bit, OAuth is a complex subject!

Authenticate client-side app to REST API using CORS with local strategy

The Problem:
Serving a secure API to a client side app using only a local authentication strategy. The red arrows are part of the knowledge gap.
Context:
That is --- client.example.com is making a POST to api.example.com/login where on success client.example.com can gain access to a GET service like api.example.com/secret.
An idea!
Implimentation of OAuth 2.0 with hybrid grant type sitting in front of API.
Why hybrid?
It wouldn't be an Implicit Grant Flow aka Client-Side Web Applications Flow because there is no redirection to API server too grant access token. (i.e.) "Is it ok for so-and-so to access your data?"
It wouldn't be a Resource Owner Password Flow because a Client ID and Client Secret are passed along with the request so it's assumed the client app is server-side.
OK... so what about a little bit of both?
What if we used a CRSF token on page load of client-side app, and POST it with user credentials too OAuth 2.0 authentication endpoint to exchange for access token? You would authenticate each subsequent request with the access token and CRSF token after a successful login.
A good Node.js OAuth 2.0 library I found:
https://github.com/ammmir/node-oauth2-provider
Help Me!
I can not find a working example of an authentication measure that solves this problem! Point me in the right direction?
Ultimately, the goal here is too authenticate a client side app to a REST api using CORS with a local strategy --- i.e. username & password --- even if the convention above isn't possible.
To Accommodate Bounty:
This is a client side app, so let's stay trendy.
I'm looking for a working example using the Node.js OAuth 2.0 seed above for the API/Auth server and a front end framework like Angular.js or Backbone.js to make requests.
The example should match the context described above.
I'm working on an app with a pretty similar architecture though the services are .NET Web API rather than Node and we're using DotNetOpenAuth for the OAuth provider. Rather than the hybrid approach you're suggesting we're doing the following:
x.com serves up a login page
login page POSTs back credentials to x.com
server side logic at x.com combines client_id and client_secret with the credentials to submit a token request (resource owner password credentials grant that you've
mentioned above) receiving back both a temporary access token and a
refresh token
the refresh token is encrypted into a cookie issued by x.com
both the cookie (with encrypted refresh token) and the temporary access token are then sent to the browser
the client app (angular in my case) can now use the access token to hit api.x.com for services (It appears you're well aware of the limitations of CORS... we hacked a version of angular's $resource to facilitate this but it wasn't pretty since we wanted to use all HTTP verbs and support IE9)
when the access token expires, the client side app can request a new access token from x.com
server-side, x.com decrypts the cookie to get at the refresh token and issues another oauth call for a new access token
This is fairly high-level but hopefully gives you a sense for how to tackle your situation. In my case, and it appears in yours, we didn't want to use session state or a database to store the refresh token but obviously exposing that to the browser introduces security concerns so the encryption of the refresh token is important (among other security considerations) and the use of the cookie eliminates the need for session state or other persistent storage on x.com.
Not an answer running for the prize. Just my 2 cents :)
On my web server,
I do my authentication through a rest call with login/password with basic authentication over https. This call delivers a key to the client (a one page web app).
Then every subsequent REST call is signed with the key. The server checks that the signature is correct and everything still happen in https.
This mechanism is quite used I believe.
I don't see the issue with cross domain. I have a single source anf if I need something from another source, I'd use JSONP.
I use nginx as an https->http forwarder.
Not sure how it competes with an OAuth2 solution.
I've built this example using Node and PassportJS to show how to authenticate the users with Facebook or Local Strategy. Both sides are on different domains as you described and it requires CORS enabled.
GitHub: https://github.com/pablodenadai/Corsnection
Live demo: http://corsnection-client.herokuapp.com/
I can't promise that I have time to write working example but I can show you 2 paths :)
The biggest deal is CORS. After you solve that problem it is easy to use $http service. So, first and probably easiest may be to configure reverse proxy in x.com webserver which points to api.x.com. I wrote article here
Second approach is better, and created for exactly this purpose, to authorise specific domain to use your resource. It involves a bit of coding in api.x.com so you don't have to change anything in new web applications served in other domains. You simply need to authorise CORS requests in api.x.com service.
Create table in database where you can manage list of authorised domains
Add in that table record "x.com"
in api.x.com add request filter/interceptor what ever tech term you use for method which should be invoked after request is handled and add in response Access-Control-Allow-Origin: x.com if request comes from x.com (in other words check in request header refer value match to any value in table above and put that value in Access-Control-Allow-Origin response header).
That is all :) After this if you know how to use $http or jQuey.ajax you will be able to POST/PUT/DELETE/... any request to api.x.com from any authorised domain in just few minutes.
I very similar idea using vinilla js web app and cross domain authentication to GAE backend or OpenID connect.
The web app is run on CDN. When click login link, it goes to respective login server and redirect back to the web app (with XSRF security token and HTTPS only cookie). Login server accept cross domain request with credentials. XSRF token has to be set (in header) with every request. cookie is set by the browser. Since it is HTTP only cookie, JS cannot read it. The technique is very secure.
Once login, you can get secure assess from login server.
For detail description, you can find here and open source repo here.

Categories

Resources