How to log out from application but not from Azure AD? - asp.net-mvc

In my Asp.Net MVC application, I have recently integrated Azure AD authentication. The login is working fine, but I am not able to get the logout right. I would like to log the users out from my application only, and not "single sign out". Everything I have read is about single sign out. Here is what I have tried:
Abandon the session and redirect to login page. An error related to missing claims is thrown on the login page on Html.AntiForgeryToken() statement
Session.Abandon();
return RedirectToAction("Index", "Login");
This has the same error as #1
FederatedAuthentication.SessionAuthenticationModule.SignOut();
FederatedAuthentication.SessionAuthenticationModule.DeleteSessionTokenCookie();
return RedirectToAction("Index", "Login");
This takes user to single sign out
HttpContext.GetOwinContext().Authentication.SignOut(OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);

Here are a few of workarounds as suggested by #Jan Hajek here
Make sure that Azure AD is listed as a Connected Service in your .NET application.
This will prevent a single logout.
We can create single sign out endpoint in controller as below example
return SignOut(
new AuthenticationProperties
{
RedirectUri = callbackUrl,
},
CookieAuthenticationDefaults.AuthenticationScheme
);
NOTE: - When we hit the logout, it clears the cookies which is correct, because the user session (in case of Razor Pages and MVC) is stored in Cookies .
Also we can set redirect logout url (e.g- "<your-doman>/.auth/logout") to the home page of the application itself where it ask for log in again .
For more information please refer the below links
MS BLOG: Expanding App Service Authentication/Authorization
SO THREAD
MS DOC: Customize sign-in and sign-out in Azure App Service authentication

Related

Check OpenID Connect user against local web application users with OWIN

I need to add OpenID Connect authentication to an existing ASP.NET MVC web application. For that, I am using OWIN.
Once the user has been successfully authenticated by the authentication server (Azure Active Directory in my case), I need to check that the user can be mapped to a local account in my web application. If the user has no valid local account, the actual login process should fail. Is there a standard way to do that?
In the sample I have found (https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-asp-webapp), it seems that as soon as the user is successfully authenticated by Azure, then it is logged in the application. Can we add custom code to the OWIN-OIDC sign-in process to perform additional checks?
Thanks for your help.
Good question and there are 2 levels here, as you are finding:
Authenticating with the Authorization Server / Identity Provider
Access to the application being allowed
If the first is successful but the second is not, then the standard behaviour is to redirect the user to an 'Access Denied' page (this page must allow anonymous access, to avoid a redirect loop). The login has not failed, but the user is not authorized to access the app.
OWIN uses response_type='code id_token' meaning that you will have an Open Id Connect User Identity to work with. If you can't find the user in your own data you can perform a redirect in the below callback:
var openidOptions = new OpenIdConnectAuthenticationOptions
{
Notifications = new OpenIdConnectAuthenticationNotifications
{
// Point to a callback to check the identity against my own system
// This callback can perform a redirect if the app does not recognise the user
AuthorizationCodeReceived = HandleAuthorizationCodeReceived
}
}

Attempting to connect to a IdentityServer4 login page fails

I have an existing website that I want to do a proof of concept with OAuth2 / OIDC. To this end I've configured a locally running IdentityServer4 MVC app as my demo OIDC server following the IdentityServer4 quick setup guidelines. This works fine and navigating to:
http://localhost:5000/.well-known/openid-configuration
Lets me see the discovery document.
I have created a fake login page on this OIDC app which consists of just a login button with no user credentials required.
There's no actual user database and I'm just hard coding some user details to return when the 'authentication' occurs.
In my pre-existing site I've added the OWIN middle wear and am configuring OIDC using the OpenIdConnectAuthenticationOptions. The clientId, scopes, secret etc all match as required and the authority is set to point to my locally running demo OIDC app (http://localhost:5000). The redirect url is set to return to my pre-existing site once authentication is complete.
This all appears to be fine but here's what I want to achieve and can't get working. On my pre-existing site when I navigate to any page that requires authentication I want the user to be redirected to the login page I created on OIDC app. They click the login button (no user details required) and are authenticated and redirected back to the original page.
Currently when I navigate to a protected page I am successfully redirected to the OIDC app but I am redirected to an error page and I don't know why. The error page gives me no detail, it's actually hard coded in the app.
When I look at the discovery document I see that the setting for the 'authorization_endpoint' is set to:
http://localhost:5000/connect/authorize
So I thought maybe I needed to either change that to point to Home/Login which is where I've created the dummy login form, or else I needed to actually create that connect/authorize endpoint and put my form there. Creating the end point makes no difference, it never gets hit and instead I just get the error page on my OIDC app. Changing it to home/login also appears to be ignored.
I am away from my main PC at the moment hence the lack of code snippets but essentially the set up is as per the IdentityServer4 quick setup guide and the OIDC app does appear to be working.
The issue is getting my pre-existing site to properly redirect to the login page.
I've been stuck on this for quite a while now and would like to even get to the stage of seeing the dummy login page. Any pointers are appreciated and again apologies for the lack of sample code.
UPDATE
I've got the login form appearing by setting the openidconfiguration like so:
Configuration = new OpenIdConnectConfiguration()
{
AuthorizationEndPoint = "http://localhost:5000/home/login"
}
However, this isn't logging me in when I click login. On that login action I'm doing this:
await HttpContext.SignInAsync("subjectId","username", authenticationProps);
And then redirecting back to my existing site. However it's not authenticating me and the redirect ends up being redirected back again to the login page.
UPDATE 2
I think the redirect URI should possibly be doing something more. Currently I do the following:
Try and access a restricted page -> Redirected to OIDC server -> Click Login (this sets the subject and user successfully) -> Am redirected to redirect URI which immediately bounces me back to the OIDC server.
So maybe the redirect URI is supposed to confirm login or otherwise do something?
So in the open id connect protocol, the authorize endpoint is used to validate the client information passed as query parameters (client_id, scopes, redirect_uri, etc). In your authentication server, none of that is being checked if all the endpoint does is return a form. Then again the validation can be tedious so keeping the authorize endpoint separate from the endpoint for logging in might be worth a thought.
The developers of Identity Server thought the same thing which is why they set up the endpoint (and endpoint validation) for you as part of the middleware. The validation uses the components that were injected (primarily the client_store, and your defined scopes) to be used by Identity Server.
After the framework validates your authorize request using your client store implementation, it will redirect the user to whichever login page you specify. The login page can be specified by changing it with the a delegate that can be passed in as the second parameter of 'AddIdentityServer' (that takes in something of type IdentityServerOptions that we'll refer to as just 'options'). In the delegate you can specify the login url of the page by changing the value of 'options.UserInteraction.LoginUrl' to the url of the login page.
After the user logs in and you call the signInAsync method on the HttpContext, you're actually supposed to redirect back to a query parameter passed to the login page referred to as the 'return_url' (which is basically the initial authorize endpoint request). This authorize endpoint further validates the cookie and will send the user back to the 'redirect_uri' (if consent on the client is set to false) with a code (if using the authorization code flow) or the id_token and optionally the access token (if using the implicit flow).
Assuming the implicit flow for simplicity, the tokens can be found in the request to the 'redirect_uri' and from there it's all up to you. Commonly the client will issue some kind of cookie (which can potentially contain the id or access token) to mark the successful authentication by the identity provider.

How to incoporate facebook login MVC 5 identity 2?

In an account controller there is ExternalLogin action.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
I have appId and appSecrete set in StartUp. I'm running my application in local machine(localhost:port). I have custom facebook button(normal button).
I do not understand how I can call the external login from the button since the action above requires provider and return url. There is already ExternalLogin callback action as well.
When setting facebook application, I seem not to understand this part: Valid OAuth redirect URIs.
Please somebody help. The articles I read confuse me in this regard.
Edit: I can now connect to facebook, the only problem is I get an error:
URL Blocked: This redirect failed because the redirect URI is not
whitelisted in the app’s Client OAuth Settings. Make sure Client and
Web OAuth Login are on and add all your app domains as Valid OAuth
Redirect URIs.
I believe it is caused by Valid OAuth redirect URIs which is not set correctly.
Go to you facebook app development website. Open your page. Go to Facebook Login option under it go to setting option and change your Valid OAuth redirect URL. you have to give your website valid path in that section.
For local host you can try like this -
http://localhost:8080/fb_login_local/

ASP.NET Core API Facebook registration/login

I'm working on ASP.NET Core API and need to add option to register with social service, e.g. Facebook.
Scenario should be e.g. Android App user should click "Sign up with Facebook" at startup, then Facebook app should open then user should click confirm and be registered in app.
Default Asp.net web example shows how to do it with web page on same host as APIs with return Challenge() response that basically returns HTML page as I understood.
What is the correct flow here and is there any existing libraries to do that?
As I understand now flow is something like this:
1. API server has my AppId and AppSecret from Facebook
2. Android app should request "applciation token" from API server
3. Android app should call Facebook with that token and get "user confirmation token"
4. Android app should pass "user confirmation token" to API server
5. API server should call Facebook with "AppId/AppSecret token + user confrimation token" and get details about user and create local user in database.
6. API server should create "API token" for that user
7. Android app should use "API token"
So at least I want to understand what to replace this code from example with:
public IActionResult ExternalLogin(string provider, string returnUrl = null) {
// Request a redirect to the external login provider.
var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
the thing to understand is the default web app template provided in VS 2015 uses ASP.NET Identity with cookie authentication. cookie auth works for web browsers but for an android app to authenticate you would need something to issue jwt tokens in addition to or instead of cookies. There is nothing built in provided by Microsoft for that in asp.net core, the recommendation is to use IdentityServer4

Single Logout second request fails in ADFS

I have setup when I'm using Auth0 to SSO through ADFS into a Spring Security Application, using saml all the way through. So, it looks like this:
Auth0 ----> ADFS ----> SpringSecurity App.
I have control of Auth0, but it's simulating a third party that would integrate with our ADFS server.
This works fine.
To do logout, I minimally want to destroy the sessions on the SpringSecurity App and the ADFS application. When I call logout the first time, a SAML logout request gets passed to ADFS. ADFS then passes a Logout request to Auth0, where it ends. This destroys the session.
However, if, without closing the browser, I SSO in again and then logout, ADFS rejects the logout request with a urn:oasis:names:tc:SAML:2.0:status:Requester, meaning it didn't like something about my request.
I've been able to narrow it down to the fact that there is a SAMLLogout cookie, with a domain of my ADFS server set. This appears to be set when the logout bounces over to Auth0, but is never removed. Calling logout when that cookie is present causes an error. When I delete that one cookie, I can logout successfully (in that it destroys the ADFS session and sends a logout request to Auth0). The cookie has a Session expiration, so closing and opening the browser also works.
I can post token requests and responses, but I don't think it has to do with the tokens itself. They are all signed correctly, and ADFS doesn't report any errors when doing the logout.
I don't necessarily need ADFS to call out to Auth0 (or any IdP) to destroy that session, I simply need it to destroy it's own session.
Instead of using destroy kindly use "unset" to avoid further problems with regards to your log out code. I don't know why but there are times that "destroy" causes problems with regards to log out button.
We came up with the following solution:
we have noticed that the adfs/ls/idpinitiatedsignon.aspx page has the "Sign out from all the sites that you have accessed." option that will kill the authentication cookies no matter how many times you logged in and logged out from different tabs.
We have create a duplicate copies of the idpinitiatedsignon.aspx and the idpinitiatedsignon.aspx.cs and renamed them (example logoutpage.aspx).
We have added the SingleLogout(); at the end of the Page_Init of the idpinitiatedsignon.aspx.cs so it reads:
protected void Page_Init( object sender, EventArgs e )
{
string rpIdentity = Context.Request.QueryString[RpIdentityQueryParameter];
//
// If the query string specified a certain relying party, sign in to that relying party.
//
if ( !String.IsNullOrEmpty( rpIdentity ) )
{
string decodedIdentity = Server.UrlDecode( rpIdentity );
if ( decodedIdentity == IdpAsRpIdentifier )
{
decodedIdentity = String.Empty;
}
SignIn( rpIdentity, new SignOnRequestParameters() );
}
else
{
PopulateConditionalVisibilityControls();
RelyingPartyDropDownList.DataSource = RelyingParties;
RelyingPartyDropDownList.DataBind();
UpdateText();
}
SingleLogout();
}
then we referenced this new page example logoutpage.aspx as the logout URL.
I hope this will save some research time for others who are facing this SAML Logout issue on ADFS v2.

Resources