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

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.

Related

What is the right way to start Authorization Code Flow from identity server login page?

I'm trying to implement Authorization Code Flow for SPA React client with ASP.NET Core and IdentityServer4.
There are two scenarios:
1) User open SPA app, we check if he has an access token and if he hasn't we generate url like
/connect/authorize?
client_id=*client_id*&
redirect_uri=*redirect_uri*&
response_type=code&
response_mode=fragment&
state=*some_state*&
nonce=*some_nonce*&
code_challenge=*code_challenge*&
code_challenge_method=S256&
scope=openid profile email
And so Authorization Code Flow starts. This works pretty clear and after all round trips user comes back to SPA app with code then send request for token (include code and code_verifier) then receive it and with happiness in soul continue using our great application.
2) User opens login page directly and here is where I'm stuck. IdentityServer context knows nothing about this user, code challenges etc. because we didn't make request to /connect/authorize before going to this page as in normal flow. What's next?
I can generate /connect/authorize link directly in login page and do ugly redirect to it and then back to login page (what I don't want to do honestly), but how my SPA app will know what code_verifier I generate here? Of course I can store it in some shared cross-domain cookie, but here should be something better approach I believe.
Another solution I can redirect user from login page to my app, it recognizes that user not authorized and we start scenario #1. Also not my go to approach I think.
What should I do in case user opens my identity server page directly?
Is this possible using Authorization Code Flow or should I consider combine some other flows with this one?
I don't want to use Implicit Flow due to new recommendation from OAuth 2.0 specification.
Quite a simple answer to this - in your second scenario - if your user opens IDP login page directly, they didn't want to go to your app. It's the same if you were using Google or Facebook or one of the other known IDP's for your SPA and as a user I just went to their login page instead. They couldn't possibly know if my intention was to ever come to login so that I am later redirected to your SPA.
Now having said all that - what you could do to make this work somewhat seamless - is to redirect to your SPA's protected page after the user logs in through Identity Server 4 (that's simple because you own the login pages and there is no OAuth involved here). Your SPA would then be triggered to initiate the OAuth2 flow and would redirect back to Identity Server 4. The user has already logged in just seconds ago here though, so the login procedure would be skipped and user would either be presented with consent page or if your client is configured to skip consent page - user would be redirected back to your SPA with the usual tokens and such.
So to break it down into the flow:
User Accesses IDS4 Login Page -> User Enters Credentials -> IDS4
Authenticates User and Redirects to your SPA protected page -> Your
SPA initiates OAuth2 flow and redirects back to IDS4 -> IDS4 displays
consent page -> IDS4 issues auth code back to your SPA.
There is ofcourse extra step here that your SPA will exchange auth code for access token, but I omitted it for clarity purpose as it's not relevant to the question.

How does Single-logout works with OKTA(IDP)-Shibboleth(SP)-App

OKTA-Shibboleth(Apache)-Nakisa(Tomcat)
SSO is working for logging-in.
Now, I need to configure Logout. So, user logs out from the app, user needs to be redirected to OKTA page with tiles.
But, currently,user is redirected to the app again.
It's sending user to /logout?redirect=default.html but that default.html is captured by Apache rule and logging user back in.
It looks like it needs to hit
https://xxxx/Shibboleth.sso/Logout. When I access this url, it says logout is successfully done although it's not going back to OKTA. Does that mean that in the App's logout setting, they need to redirect to this?
But, how do I make user to go back to IdP(i.e OKTA) again?
This is what I assume that will happen.
Logout button click > logout from Shibboleth > return to OKTA so user can click other tiles.
Something to configure Shibboleth2.xml?
Document says i just need to configure the following which is there by default.
<!-- SAML and local-only logout. -->
<Logout>SAML2 Local</Logout>
But, how does it redirect user to OKTA(IdP) once user log out completes.
Is it configured in IdP's metadata ?
You can redirect the user after a local logout event anywhere you'd like, via passing the ?return= parameter a URL-encoded destination, i.e. you should update your logout link to:
https://xxxx/Shibboleth.sso/Logout?return=https%3A%2F%2Fgoogle.com
in order to redirect folks to Google once logout has taken place.
Now, you only need an Okta URL to return folks to... so I think if your client's Okta tenant is "foobar.okta.com", redirecting them after local logout to the Okta login page shouldn't prompt them to login, since they will already have the Okta Session... so maybe try:
https://xxxx/Shibboleth.sso/Logout?return=https%3A%2F%2Ffoobar.okta.com%2Flogin
Of course, you'll need to test that... but it should work, and on the off chance that the user's Shibboleth SP session was active, and their Okta session invalidated through some other mechanism, that'll just return them to their regular Okta login page.
You can obviously redirect them to any endpoint with the return parameter, for example, whatever Okta's logout URL (if you wanted to kill their Okta session too).
The only logout that's configurable by Metadata is SLO (single logout), i.e. if you wanted it to, Shibboleth can redirect the user to Okta after they complete the logout of the SP session, along with a specially-craft <LogoutRequest> assertion payload, which Okta would parse and act on in any number of ways, i.e. killing the user's Okta session, propagating Okta-initiated subsequent <LogoutRequest> assertions to other Service Providers, etc. In practice, this never really works, because such configurations are very difficult to get working between all of the relevant parties.

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.

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

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.

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

Resources