I've run into an issue when using OAuth 2 authorization codes in an web app's URL, such as is returned by Google's OAuth method (https://developers.google.com/accounts/docs/OAuth2Login).
I've been using the google redirect method; where you redirect the user to a Google URL, passing in client_id and redirect_uri. The user authenticates and the authorization code is passed to the redirect_uri as a
The issue is that the access code stays in the page URL, so if the user bookmarks or posts the URL, they are sending an invalid Authorization Code.
Eg:
http://myapp.com/?code=kACASDSDdAS81J5B8M_owCyUNgV46XdZaqBBMh4T8OJFEKPRrgN7gtiFOcMW5Fv3gk
What is the best way to handle this case? Ideally, I would like to send the authorization code in a POST body as it isn't visible to the player?
I've spent a bit of time looking at Google App Engine (the platform I'm using) to redirect the user, but can't seem to send a POST body in a redirect.
After the user is directed to your app with the authorization code in the URL query parameter, you should:
1) Exchange the authorization code for an access token by making a HTTPs POST to Google's OAuth 2.0 token endpoint and save that access token as appropriate (datastore, memcache, etc)
2) Redirect the user to a URL without the ?code. You can't send a POST body in a redirect (HTTP doesn't allow it), but that shouldn't be necessary if you store the access token server-side for making API calls.
If you must make the token accessible client-side, you can:
a) Send it back as a cookie along with the redirect (which exposes it to the client, though you could encrypt it) OR
b) Generate a HTML form, with JavaScript for auto-submitting it instead of doing the redirect. Kind of ugly, but common.
Related
So I would like to add SSO using Azure AD.
My stack consists of a React app as frontend and a NestJS API as backend(decoupled). The scenario looks like the following
User clicks login button
I create a new window (popup) which leads the user to the Azure AD login page (step 3 in the diagram)
After the user logs in to Azure AD successfully Azure AD will POST the SAML response to a redirect URL I have provided them (let's say http://myapp.com/saml) (step 5 in the diagram) (This redirect URL, to my understanding, has to be an endpoint in the NestJS api since React frontend can't handle post request.)
NestJS will handle the post request get the info it needs, validates etc etc and then NestJS has to return a Token to the frontend somehow in order for the frontend to store that token in a cookie and be able use it in subsequent requests to the NestJS api so that NestJS will be able to know that this user is logged in. (Step 6)
My issue with this approach is that I don't know how will the client get the token when the the validation is completed from NestJS. If this was a coupled application this would not be an issue since the backend would handle the post request set a cookie and redirect the user. But in this case NestJS can't redirect the user since react handles the routing.
What is the correct approach to handle this?
I thought maybe this could work by using websockets...so that when NestJS handles the post request it can send a message to the user which message will contain the token and then the frontend can add it to a cookie and redirect the user to a protected page.
(1) Does the frontend really need to store a token to send to the backend on subsequent requests? What if the backend set a cookie at Step 7 in your diagram? Then the cookie would be sent to the backend on subsequent requests. The cookie would be scoped to the backend’s domain name and path. Keeping tokens away from the frontend has the advantage that you can keep them safe from being accessed via a Cross-Site Scripting attack on your frontend, if your backend’s cookie has the HttpOnly attribute set.
(2) If you still need to communicate info from the backend to the frontend in Step 7, then send an HTTP 301 response with the Location header set to your frontend’s URL with the info you want to communicate included in the query or hash portion of the URL. For example, after validating in Step 6, in Step 7 your backend’s HTTP 301 response could have the Location header set to https://my-frontend-domain.com?user=bob or set to https://my-frontend-domain.com#user=bob. With ?user=bob, user=bob would get sent over the network again when the browser requests https://my-frontend-domain.com/?user=bob, whereas with #user=bob it would not. Then the frontend’s JavaScript can read user=bob from the URL.
I am trying various Java Spring based security implementations as follows
1. JWT Authentication
User access /
Springboot identifies as protected resource and redirects user to /login
User enters credentials and browsers does a POST to /authenticate
Server validates the credentials and generates JWT token. Set into response header and redirects to /
Browser loads /. AngularJS recognizes JWT token in the response header and stores the same in the localStorage
All subsequent calls will have the Bearer token in header (injected through httpInterceptor)
Note: Stateless Session
2. OAuth2 authentication
User access /
Springboot identifies as protected resource and redirects user to /login
/login is intercepted by Spring security. Redirects to Oauth2 authorization server with a generated state and redirect URL back to application
User enters credentials
Oauth server redirects back to application URL "/login?code=xxx&state=yyy"
/login is intercepted by Spring security. Recognizes the code and state, generates Cookie and sets in response header. Redirects to /
Browser loads /. Browser recognizes cookie in the response header and stores the same.
If a call is made to /user, the Principal object is populated with the JWT which I am able to extract as follows
#RequestMapping(value= {"/user")
public ResponseEntity<Map<String, String>> user(Principal principal) throws Exception {
OAuth2Authentication obj = (OAuth2Authentication) principal;
authentication = obj.getUserAuthentication();
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) obj.getDetails();
String jwt = oAuth2AuthenticationDetails.getTokenValue();
All subsequent calls will have the Cookie in the Request
Note: A Stateful Session is created in server side to store the session details. This required to decrypt the cookie and identify the user
Now I want to implement security using Oauth2+JWT but stateless at same time as follows
3. OAuth2 + JWT + Stateless
User access /
Springboot identifies as protected resource and redirects user to /login
/login is interecepted by Spring security. Redirects to Oauth2 authorization server with a generated state and redirect URL back to application
User enters credentials
Oauth server redirects back to application URL "/login?code=xxx&state=yyy"
/login is intercepted by Spring security. Recognizes the code and state, extract JWT token by invoking
OAuth2AuthenticationDetails.getTokenValue() and set in response
header. Redirect to /
Browser loads /. AngularJS recognizes JWT token in the response header and stores the same in the localStorage
All subsequent calls will have the Bearer token in header (injected through httpInterceptor)
Question
I am trying to figure out how to implement the highlighted step above
Just an idea/direction, if I got you right:
You can create a GenericFilterBean and add that to the HttpSecurity filter chain.
When using JWT, there should be something similar (a filter, which extracts the bearer-token from the header) and then populates an Authentication object for Spring Security.
So the new filter could grab the token from the request and set the response accordingly.
You could also handle that in an unprotected (!) callback endpoint like login/callback?..., which than sets the cookie for you.
In our application, the server (spring boot) is totally stateless and does not have any oauth nor stateful stuff. Obviously it never redirects anything or has any other views/endpoints than / for AngularJS (and some REST-APIs under /api/...). Thus, the OAuth-flow is totally handled by AngularJS, which in turn checks the callback from the oauth-server and locally sets the JWT-Token (like in your first approach). In first versions we also tried to mix up redirects with stateless JWT and stateful sessions etc., but this led to very strange behavior with the logins - the state (logged in or not) was not always clear and in some cases redirects were wrong etc.
This might help you implement your desired solution.
The author proposes that once a user successfully authenticates with Oauth2 providers (Google etc), you send a short-lived token as a URL param to your frontend application and use this short-lived token to exchange it for a longer-lived token.
I want to make an oauth2 login with Twitch on my website and I have an angular2 website and I'm working with router links.
When I want to log me in with twitch acc to say yes it is me and so everything is fine. Ok the end not xD
When i go to the twitch oauth2 for authorizing i need an redirectUri. My problem is now how can i make this in angular2? Because I can't type www.page.com/app/afterlogin/afterlogin.php or somethink like that.
I need this because I need from the user the access token, I dont want that he need to authorize himself x times.
Maybe this helps for helping me:
https://api.twitch.tv/kraken/oauth2/authorize?client_id=[client_id]&redirect_uri=http://www.page.com/app/AfterLogin/afterlogin.php&response_type=code&scope=user_read
I hope someone can help me with redirecting and some oauth2 logins :)
Let me assume a RESTful backend with Single Page Application front and answer the question. The process in general is like the following
Your SPA --> Your Server --> Your Provider --> Your Browser --> Your Provider --> Your Server --> Your SPA
Your SPA => initializes login and passess redirect_uri
Your Server => Stores redirect_uri in a cookie and sends request to
provider
Your Provider => Gets Success and Failure Urls and loads login page
to your browser
Your Browser => Loads the provider login page
Your Provider => Sends request to your server success or failure
handler
Your Server => Extracts the redirect_uri and redirects the browser
to it
Your SPA => Gets afterLoginUrl from redirect_uri and route the
user to it
Below are the steps to achieve this
When your front end sends the authentication request to your server,
append the redirect_uri. In that url, pass a afterLoginUrl query
parameter. That is used by your front end SPA to route the user to
the specific page that triggered the login. (i.e. If the request has
been triggered by a user trying to access
{base_uri}/profile/project/projects for example, it is a good
practice to route the user to this page rather than to the default
page that a normal login takes to like base_uri/profile/about). As
a result you will have a url that looks like the following.
`http://localhost:8080/oauth2/authorize/google?redirect_uri=http://localhost:4200/oauth2/redirect&afterLoginUrl=/profile/project/projects`
port 8080 being for the back end and 4200 for the front end.
Since you are using a RESTful service, you don't have a way by which you can save the redirect_uri on your server (since REST is stateless). Because of this you need to send it with the request you send to the provider as a cookie.
When the success is received from the provider, you will know which route of your SPA to hit by extracting the cookie you sent. Then you dedicate a route to handle your request from your own server (in my case oauth2/redirect) in your front end app.
On the component specified for the route in step 3 you will receive token and afterLoginUrl(if there is). You will have something like the following on the url
http://localhost:4200/oauth2/redirect?afterLoginUrl=/profile/project/projects&token={token value}
Verify your token, check whether or not there is afterLoginUrl and redirect to the route specified by afterLoginUrl if there is one or to the default profile page if there isn't.
I think a wonderful resource can be found here.
Authorization Code Grant flow is just one of several ways of how you can use OAuth2. It's not suited for applications running in a browser, because it requires a client secret which you cannot keep safe in a browser.
There is another flow - Implicit flow which is meant for JavaScript applications - you get an access token and/or ID token in a redirect URI - in the hash part (#...) so they don't get to a server. Then you can easily use any Angular route path as a redirect URI. So the redirect URL from OAuth2 server could look something like this:
http://example.com/myAngularApp/afterLogin#token=...
When you get to that URI, you just save the token and change the route to some real form.
I have a MEAN stack application that uses JWT for authentication. I use Satellizer (an Angular module) to implement the JWT authentication flow. It sends the Authorization header with the bearer token whenever requests are made through the app.
My problem is when a request to a restricted url, lets say /dashboard is made through the address bar, the browser does not send an authorization header, thus blocking the request.
So what should I do to make the browser send the authorization header when making requests to restricted urls?
If the user directly enters an address in the bar I'm afraid you can not do anything except redirect to the error form. The browser will not send specific headers
But if you are building the links that the user clicks, then you can add the JWT token in the URL link itself /dashboard?jwt=. In the server you will have to take into account this case of authentication
Be careful in this case, the browser could cache the URL and write it in some log. If the JWT is signed and not encrypted it could leak sensitive information if an attacker has read-access to the log files. Also it could be possible a session hijacking attack.
I am having troubles in implementing OAuth in the right way.
I use a client/API architecture (Angular for front and Node.js for back) and I would like user to sign in using Google OAuth authentication only.
Here is what I think is the right way for the moment (tell me if I misunderstood something) :
Angular open a Google popup asking user's consent.
Once the user agree, Google Authorization server sends back to angular a verification code.
This verification code is forwarded to an API endpoint.
Then, the API asks Google Authorization server to exchange this code for an access_token, an id_token and a refresh_token.
Google sends those 3 tokens.
The API uses access_token to retrieve user from Google API
The API persists the user
Here is the little dillema, in my opinion, the access_token and refresh_token should be stored into the database and the id_token should be sent back to Angular client.
This way, it would allow the API to ask for resource in Google API and if the token expires it can still ask for a new token thanks to the refresh_token.
Client-side, the id_token is embedded in all requests thus allowing the API to identify the client and verify his authentication with Google certs from https://www.googleapis.com/oauth2/v3/certs.
Supposing this is right way to use tokens, how could I deal with id_token expiration since client does not have any refresh token ?
Thanks !
I do it slightly different (I have the same basic architecture though).
Angular decides the user needs to log in and displays a login popup.
The url in the login popup is not serviced by angular, but is instead directly run off of the backend server: /auth/google . (I use hapijs and bell, personally).
/auth/google is serviced by a bell plugin and initiates the OAUTH dance.
the end of the OAUTH dance results in my node server generating a local token (I just generate random bytes and store them in redis mapped to user ids)
because the initial login popup was created by window.open, the success page (generated on the api side rather than in angular) can use window.opener.postMessage to communicate the token back to the angular runtime.
This way, all my sensitive google credentials (the user's oauth token, refresh token if needed, and my application's api ID and secret) are only on the server, except for during the OAUTH dance relay when they're in a URL string during the client redirects. This is reasonably secure.
Then for all the actual user interactions with the api, I use the token I generated in step four to authenticate. This could be a JWT if you wanted, but I don't do it that way; I just use redis to map from 'longrandostring' -> userId. That lets me (for example) force everyone to re-login if I wipe the redis database that has all the tokens stored, or I can write a lua script to delete all the entries that map to a certain userid.
If you need a refresh token, you can set access_type=offline in the initial request to oauth2/auth, and you'll get a refresh token as part of the response, unless you've previously gotten a refresh token. You can then persist it on the server side and get new access tokens as needed. If you set approval_prompt=force as well, you'll force a new consent screen and be guaranteed a refresh token (but after some small number of refresh tokens granted to a user, older ones expire on the same application so it's best to only request them if really needed).