How can i redirect to an Angular router link with oauth2 login? - angularjs

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.

Related

Azure Active Directory SSO (SAML protocol) when frontend is decoupled from backend. How will frontend be notified that validation is completed?

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.

What is PostLogoutRedirectUris and signout-callback-oidc in signout flow

what is the purpose of
PostLogoutRedirectUris = { "https://yourclienthost:port/signout-callback-oidc" }
in Client Config please?
When I put it, IdentityServer logout page show confirmed logout message AND shows a link to redirect back to my application.
When I omit it, IdentityServer show confirmed logout message BUT NOT shows a link to redirect back to my application
Regardless of the options, IdentityServer still logs me out. So I am confused what that config does. Please help clarify. Thank you very much!
The purpose of PostLogoutRedirectUris in client config is security.
When the client initiates signout, the client opens a url like that:
https://[identity-server]/connect/endsession?post_logout_redirect_uri=https%3A%2F%2Fclient%2Fapp%2Fsignout-callback-oidc&id_token_hint=XXX&state=YYY&...
Pay attention that the client itself tells IdentityServer where to redirect to after signout. This means that the client might cheat IdentityServer to redirect to a malicious URI.
That's why a white-list of URIs is used on the IdentityServer side, which is called PostLogoutRedirectUris. The list contains all possible deployment locations of the client e.g.
"PostLogoutRedirectUris": [
"https://localhost:44321/signout-callback-oidc",
"https://client/app/signout-callback-oidc", ...
]
When the callback URI suggested by the client is not on the white-list, then
IdentityServer shows confirmed logout message but NOT a link to
redirect back to your application
The signout process continues like that:
.../connect/endsession above redirects to https://[identity-server]/Account/Logout?logoutId=ZZZ
The Logout action renders a page with the link back to the client ( https://client/app/signout-callback-oidc&id_token_hint=XXX&state=YYY&... )
On the same page there is also an iframe wich opens https://[identity-server]/connect/endsession/callback?endSessionId=TTT
This iframe renders another iframe which signs out the client by calling https://client/app/signout-oidc?sid=SSS&iss=https://identity-server
On the same page there is also some JavaScript which automatically opens the link from step 3 and goes back to your client app.
The /signout-callback-oidc route in a MVC client does nothing (probably; I don't know what it does) but it redirects to the home page. (*)
The signout from both IdentityServer and the client app is already complete in step 5. /signout-callback-oidc has nothing to do with the signout itself. It only provides a landing page back in the client app so that the user does not get stuck on a page in IdentityServer.
(*) More precisely, /signout-callback-oidc in the client app redirects to options.SignedOutRedirectUri which can be set during AddOpenIdConnect().
By defaut SignedOutRedirectUri == "/". I prefer to change that option to "/Account/Login". In this way I see the login page right after signout.
"/signout-callback-oidc" is just the default value of options.SignedOutCallbackPath. You can change that path but there is no benefit. This route is automatically registered and automatically handled by the middleware. You cannot write your own controller/action for that route.
PostLogoutRedirectUri is meant to redirect you when you log out of your client. It will, e.g. bring you back to your client application's home screen. When you implement a 'single' logout for your application, you stay logged in to IdentityServer.
Once you are logged out of IdentityServer itself however, no PostLogoutRedirectUri is used and the flow ends on the IdentityServer UI.
In this case, it probably means that you implemented your signout by calling SignOutAsync twice. Once for the local application and once for the IDP but I can't be sure without seeing your code.

Blank page after login using bookmarked authorization URL in IdentityServer4

We have discovered that our users very often for the first time visits our web application by browsing the direct URL of the OIDC client (https://oidcclienturl.com/), The ASP.NET Core OIDC authentication middleware kicks in and the user gets redirected back to Identityserver 4 login page.
Everything works fine but then they decide to add the (temporary? state, nonce, cookies...) authorization URL as a bookmark in their browser before entering their credentials and continuing back to the web application.
This causes an issue when the user later uses the bookmark in a new session. The login seem to actually work after entering valid user credentials even if the user uses an old authorization URL, but when the user gets redirected back to the web application they end up on a blank page (https://oidcclienturl.com/signin-oidc).
After the blank page have been loaded the user is able to browse the direct URL (https://oidcclienturl.com/) sucessfully and appear as an authentcated user in the web application.
Any ideas whats causing the blank page?
That blank page shouldnt exist, if I understand it correctly its the default callback path of the oidc authentication middleware in ASP.NET Core.
Unfortunately, the real-world problem of users bookmarking the login page isn't handled cleanly by OIDC, which requires the client app to initiate the login flow.
I've addressed this by adding a RegistrationClientId column to my user data table, which is the Identity Server ClientId corresponding to the client app that called IDS when the user account was created. In the client app configuration, we use the custom Properties dictionary to add a URI fragment:
new Client
{
ClientId = "some_client",
ClientName = "Some Client",
ClientUri = "https://localhost:5000",
Properties = new Dictionary<string, string>
{
{ "StartLoginFragment", "/Auth/StartLogin" }
}
// other config omitted
};
When a user logs in, an empty return URL indicates IDS wasn't called by a client app, so we use RegistrationClientId to query IClientStore, then we combine the ClientUri and StartLoginFragment URIs and use the resulting URI to redirect the user back to the client application.
Over in the client application, that endpoint kicks off the OIDC sign-in flow, and since the user is already signed-in on IDS, it comes right back to the correct location in the client app. The controller action looks like this:
[HttpGet]
public async Task StartLogin()
{
await acctsvc.SignOutAsync();
await HttpContext.ChallengeAsync("oidc",
new AuthenticationProperties()
{
RedirectUri = "/"
});
}
The call to SignOutAsync just ensures any client-app signin cookies are cleaned up. It's in our custom account service, but it just runs HttpContext.SignOutAsync on the usual "Cookies" and "oidc" schemes. Normally that would also result in a signout call to IDS, but the redirection by the subsequent ChallengeAsync replaces the pending signout call.
The downside is that the action is an HTTP GET meaning pretty much anyone could theoretically trigger this action. At most it would be an annoyance.
In the special case where your IDS is only handling auth for a single client, you can skip a lot of that -- if they land on the page with no return URL, just send them to your client app start-login endpoint straightaway, before they login.

Django Rest Framework / AngularJS - Users do not get redirected anywhere after logging in, even though I have set LOGIN_REDIRECT_URL in settings.py

According to this link: http://www.django-rest-framework.org/tutorial/4-authentication-and-permissions/#adding-login-to-the-browsable-api
I need to add the following code to my URLs.py:
url(r'^api-auth/', include('rest_framework.urls',
namespace='rest_framework')),
When I added this, users can log in by going to the "api-auth" URL and using the default DjangoRestFramework login interface. After a successful login, users are directed to "/test" because I have the following code in my settings.py:
LOGIN_REDIRECT_URL = '/test'
I wanted a way for users to be able to log-in using my own custom interface but by using DjangoRestFramework's built-in code for logging users in, so I created my own template. The login form in the template sends a post request to
api-auth/login/
and sends the user object (which consists of a username and password in JS) along with the POST request. No errors are returned, so I'm assuming the login is successful. However, it does not redirect to any URL (I was expecting it to redirect to "/test").
Any idea why it does not redirect anywhere, and how I can make it redirect to "/test"?
Edit: I am also using AngularJS on the frontend.
LOGIN_REDIRECT_URL is basically from django.contrib.auth so I wouldn't except other auth backends to use it, at least not necessarily/automatically
Also if you're logging through REST say from an AngularJS, even if after the REST API login is successful and returns a redirect response, there is no guarantee that the AngularJS app will navigate to that page because the login REST API was hit using an XHR request (from $http or $resource etc)
I'm using a slightly different REST auth lib than you, called django-rest-auth (not the BrowsableAPI that comes with DRF), I'm authenticating from AngularJS, and after the call is done with success, I simply navigate the app to a new URL
djangoAuth.login(username, password).then(function(){
// make angularJS navigate to new page when login is successful
// $location.path(...) or some other way
});
Bottom line is, since you have an auth API, you can make a small AngularJS page, with login form, then when login is successful redirect with AngularJS
Worth a look
I'm using these two libs that are meant to be used together, they offer REST auth over DRF, and optional a AngularJS lib to help with the frontend
https://github.com/Tivix/django-rest-auth
https://github.com/Tivix/angular-django-registration-auth

Handling Page Reloads With OAuth Access Code In URI

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.

Resources