I have followed Microsoft website sample to setup a web authentication via Azure AD. I have tested the login via Azure AD, and it was able to logged in and out system. But, I noticed there was an issue when logging out from system. When I duplicate for multiple tabs in browser(chrome, firefox), and logout from one of the duplicated tabs. But other duplicated tabs, still remain logged in status even I refresh them. But when I open a new tab to access again, it required me to login. Below is my code for logout, does anyone know what I have missed out?
protected async Task OnLogout()
{
if (await DialogService.Confirm("Are you sure want to logout?", "Logout Confirmation",
new ConfirmOptions() { OkButtonText = "Yes", CancelButtonText = "No" }) == true)
{
await SignOutManager.SetSignOutState();
Navigation.NavigateTo("authentication/logout");
}
}
i am trying to set AWS Cognito as Auth provider to log in salesforce.
I have set up an "Auth Provider" (called it Cognito) enabled it as login option both for salesforce login and for community (in community workspace).
Auth provider settings
When i try to login to salesforce using Cognito credentials i manage to do it without issues, but when i click on "Cognito" as a login option in my community i get an error even before i try to enter my credentials:
Trying to log in
Error and url string
I use different credentials for user that is internal and user that is external, although in case of community login i did not even manage to enter my creds. In url string i see that error is "redirect_mismatch" but i am not sure what that means in current context and how to resolve it. In my Registration helper i have only adjusted "createUser" method to return a user that i already have in my org:
global User createUser(Id portalId, Auth.UserData data){
return [SELECT Id, Name, ContactId FROM User WHERE FederationIdentifier = :data.email];
}
If someone has experience with using Cognito as auth provider for salesforce communities i will be glad for any help.
Redirect mismatch is referring to your callback URL being different than the one you defined in your user pool settings. Go to user pool settings and click on app client settings. Here you will see fields for sign in and sign out urls. Put in the correct callback/redirect url here. You can enter multiple urls separated by commas.
My Auth Server uses IdentityServer4.
Redirect configured as follows for a client
RedirectUris = new List<string>
{
"https://localhost:44342/signin-oidc"
}
this works fine for those users for whom MFA is not enabled. But when it is enabled, and kicks in, the redirect doesn't work. After successful 2nd FA, user stays back on the AuthServer page.
Any idea why?
Multifactor authentication is not implemented by Identityserver4. Identityserver4 is about how the third party application gets access to protected resources on behalf of the user.
The means of how the user gets authenticated are out of the identityserver4 scope. In other words, this is not related to identityserver4.
If you're using the identityserver4 quickstart it comes with ASPNET Identity, ASPNET Identity provides you with a local authentication system for ASPNET applications. MultiFactor Authentication is probably there.
Being said that, when you try to POST to the /authorize endpoint (note authorize not authenticate) from your client application IdentityServer tries to authorize your request and to do so it makes you authenticate first, by presenting you the Login Form.
If you look at the Address bar on this point, you'll notice there's an encoded url as returnUrl param, on the controller code you'll see a check that if that param is present, redirect to that url after successful login.
So, check the flow on your application and see where does that parameter get lost on the redirect hell, at some point you're not passing the returnUrl.
I am following an older tutorial (https://code.msdn.microsoft.com/Write-Sample-App-for-79e55502) that shows how to create a web application that connects to an Azure Active Directory tenant using ADAL. There is a class library that contains DirectoryService classes that I believe does the work in retrieving AD user properties. I am wanting to create a login method for this project for security purposes, and to be able to identify what user is logged in to the application. Currently there is not a signin method that authenticates against AD by entering a username/password, so I am a little puzzled at how the app can retrieve user properties with just the AppId,Domain,and AppSecret in the Web.config without actually having someone login with either their AD creds or redirecting to login.microsoftonline/{tenantId}.....I do not know if I am making sense, but I want to be able to add a login method so a user is forced to login so it gets the claims for that specific user but if I am already using ADAL, can I also incorporate Owin?
There are two parts to your question -
1. How is this app working today, without asking end users to explicitly sign in
This sample is using Client Credential Grant, in which you don't need end users to sign in, but use the identity of an application itself to get the auth token and use it for further operations. As you mention yourself, it just needs AppId, Domain and App Secret from web.config. You can read all about it here - Client Credentials Grant
Related code to acquire token is available in MVCDirectoryGraphSample\Helpers\MVCGraphServiceHelper.cs file
AuthenticationContext authenticationContext = new AuthenticationContext(authString);
ClientCredential clientCred = new ClientCredential(ConfigurationManager.AppSettings["AppPrincipalId"],
2. How to add a login method to force users to sign in to your web application
You can start using something like Authorization Code Grant flow, instead of client credentials. Look here for documentation on different scenarios that Azure AD supports. Your case looks like a web application calling Graph API, described here
Exact code sample to do this is available here - Code Samples
I am confused about how this is used.
Most examples I've seen have it given as "/signout-callback-oidc". That seems to indicate that it uses OIDC middleware in the process. What if I want to return to a specific client page?
The automatic redirect isn't working when I set IdentityServer's AccountOptions.cs property of AutomaticRedirectAfterSignOut to true. Further, during logout, I do not receive the client's PostLogoutRedirectUri.
So, is that link supposed to go to the OIDC middleware, or is it available for use to redirect to the client?
Your client has to be configured to request the callback to one of those URIs as part of the client-initiated sign-out flow.
IS4 clients can be configured with lists of allowable redirect URIs for both sign-in and sign-out, which I'm guessing is where you see /signout-callback-oidc -- if I remember right, either the docs or maybe the Quickstart code uses that, but there's nothing special about that particular URI name. (It isn't some OIDC standard, or a "well-known" name, or anything of that nature, as far as I know.)
The missing piece of the puzzle is to configure OIDC in the client application. You didn't mention what kind of application is on the client side, but in ASP.NET Core it's an option named SignedOutCallbackPath on the AddOpenIdConnect service:
services.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = appConfig["OidcAuthority"];
options.ClientId = appConfig["OidcClientId"];
options.ClientSecret = appConfig["OidcClientSecret"];
// etc
options.SignedOutCallbackPath = "/jake-says-goodbye";
});
This causes the OIDC implementation to add a property to the sign-out request identifying that redirect URI. As long as your application properly identifies itself, as briefly mentioned in the docs here, and as long as /jake-says-goodbye is one of the approved post-logout redirect URIs on the IS4 side, you should get the callback you're expecting.
(I specifically mention "proper" identification because, based on github questions I've seen, it sounds like it might be more difficult to manage for a JS-based SPA client app versus whatever helpful things MVC does behind the scenes to manage server-to-server OIDC interaction. I can't speak to that as I've not had a need to implement any SPA clients with IS4 yet.)
The problem is that you have to set a very specific parameter in order for the PostLogoutRedirectUri to not show up as null on IdentityServer's side, and testing any of the options results in having to step through a ton of ways to set it, most of them still resulting in null. Since I'm using an older client with IdentityServer4 (in order to enable .NET 4.x webapps to authenticate through IdentityServer4, cannot easily use .NET Core with those projects - luckily IdentityServer4 is still compatible with the older client code), the action that triggers signout has two relevant things (and you'll find a TON of examples of code for that will not work for you with MVC in .NET 4.x):
Use the signout() method in this sample github repo (the IdentityServer3 MVC Owin sample client): https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/Clients/MVC%20OWIN%20Client/Controllers/HomeController.cs You can trigger that action from a button in a view.
That will get intercepted by the client's Owin middleware if you do this: https://github.com/IdentityServer/IdentityServer3/issues/2687#issuecomment-194739035 I didn't use the stored message bit, and I added the PostLogoutRedirectUri parameter in a way that IdentityServer4's LogoutRequest model wouldn't remove with this line in the same segment:
n.ProtocolMessage.PostLogoutRedirectUri = "http://myredirectaddress/ActionToDoOnceReturned";
You have to make sure that the above matches the client's PostLogoutRedirectUri on the IdentityServer side's client config or it'll be null again, and you would have missed it among all the other parameters. For instance, these methods of setting PostLogoutRedirectUri DO NOT work:
n.ProtocolMessage.SetParameter("PostLogoutRedirectURI", "some URL");
n.ProtocolMessage.SetParameter("PostLogoutUri", "another URL");
n.ProtocolMessage.SetParameter("PostLogoutRedirectUri", "yet another URL that's going to be ignored by IdentityServer4");
From there, you're off to the races because PostLogoutRedirectUri is no longer null. There are a few more considerations: check out AccountOptions in the IdentityServer controller folder. I set AutomaticRedirectAfterSignout to true there (this is used by Javascript in IdSrv's final logout page - if set, the script uses PostLogoutRedirectUri to forward the user back to the client). There's also an option to show a logout confirmation prompt, which if you want to actually display, make sure to NOT set the id token hint in the Owin (it's right next to where we set the PostLogoutRedirectUri / the part that gets triggered by signout requests). If you do those two things, AccountServices.BuildLogoutViewModel will return the prompt to the user when it's called by the AccountController.logout() method.
Thank you aaronR for the answer to my other question concerning that part:
IdentityServer4 logout (id token hint tells IdentityServer that the signout request was authorized and not a malicious person trying to harass your system / sign out users, IdSrv will ask the user for confirmation if it's not provided).
Finally, if you are confused by what's happening on the IdentityServer side in logout, and why it's repeatedly triggering the same method:
First time it gets called from the client's Owin middleware (the bit of code above that gets triggered after the Signout() action).
It uses AccountService to build a view model to return to the user for logout confirmation.
It gets triggered again by the user clicking yes on that page.
It goes through the Account service method again, which this time sets the bool to show the logout confirmation to false.
It calls the second logout method, the one with the view model that gets passed in.
This one triggers the external identity provider signout.
The external identity provider returns control back to logout, resulting in it getting called again, calling the second logout method again.
Finally, it will return the user to IdentityServer's logout page. If PostLogoutRedirectUri is set & AutomaticRedirectAfterSignOut is true, there's javascript on that page which automatically forwards the user's browser to it.
Due to having two projects to debug through at once and all of these possible ways of setting the redirect (which also have to match client/server side config in order not to be null) it can be easy to get confused.
Overview
When setting up IdentityServer (assuming it's a separate application), there are two parameters in the configuration for an accessing client: RedirectUris and PostLogoutRedirectUris. These correspond to what happens after a login or logout of a user against the IdentityServer system.
Since your client app probably has its own cookies (or JWT tokens, or whatever it's using to maintain a user session), it needs to know when the IdentityServer has processed the login and made the user data available.
The default ASP.NET OpenID Connect middleware does this with yourapp.com/signin-oidc and yourapp.com/signout-callback-oidc endpoints to intercept and handle the login/logout hand-off from IdentityServer. These endpoints have nothing to do with the OpenID protocol and can be set to whatever you want if you have your own authentication handler, but if you're using the default middleware then you must set them to that in the IdentityServer config.
Config option
However, if you still want to redirect a user after the OpenID Connect logout has completed, there's an option specifically for this:
services.AddOpenIdConnect(options =>
{
// your other options...
options.SignedOutRedirectUri = "/some-page-after-oidc-logout";
});
Microsoft Docs
I want to share how I solved problem with null PostLogoutRedirectUri value.
I always had null PostLogoutRedirectUri value in logout context until I added SignInScheme value on mvc client side.
These settings of authentication on MVC client side works for me:
var authenticationBuilder = services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
});
authenticationBuilder.AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.Cookie.Name = "identity_server_mvc";
});
authenticationBuilder.AddOpenIdConnect("oidc", options =>
{
options.Authority = "{IDENTITY_SERVER_URL}";
options.ClientId = "mvc";
options.SaveTokens = true;
options.SignInScheme = "Cookies";
});
You also need to make sure that you have added the PostLogoutRedirectUri value to the client configuration on the Identity Server side:
new Client
{
ClientId = "mvc",
AllowedGrantTypes = GrantTypes.Implicit,
RedirectUris = { "{CLIENT_URL}/signin-oidc" },
PostLogoutRedirectUris = { "{CLIENT_URL}/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
}
}
One last but important point, because of this I had null logoutId value on Identity Server side. To initiate Logout process you must first call SignOut("Cookies", "oidc") on mvc client side. Example endpoint in my HomeController:
public IActionResult Logout()
{
return SignOut("Cookies", "oidc");
}
Good luck!
Building on top of #McGuireV10 answer if your project is a Blazor WASM, then the change would be like this:
// Adds OpenID Connect Authentication
builder.Services.AddOidcAuthentication(options =>
{
options.ProviderOptions.Authority = settings.Authentication.Authority;
options.ProviderOptions.ClientId = settings.Authentication.ClientId;
options.ProviderOptions.ResponseType = "code";
options.ProviderOptions.ResponseMode = "query";
//
options.AuthenticationPaths.LogOutCallbackPath = "authentication/logout-callback";
builder.Configuration.Bind("oidc", options.ProviderOptions);
});
I ran into the same issue today; your (#JakeJ) link solved it for me. I am building a demo MVC Owin Client in .net 4.6.1 (for a third party company) so our set up is the same and our Id Svr v4 is built on net core v3.1.
I verified i had the same PostLogoutRedirectUri defined in the Id Svr side config for the client i was working on and then at the client side config too.
But i noticed that i could add a small block of code taken from the ref'ed github issue to the RedirectToIdentityProvider func delegate specific to logout.
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
{
// below is technically not needed, as it was already set for me.
n.ProtocolMessage.PostLogoutRedirectUri = LoginAndOutRedirectUri;
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token").Value;
n.ProtocolMessage.IdTokenHint = idTokenHint;
}
This means that a claim needs to be present in order for this to work so i then added the below to the SecurityTokenValidated func delegate:
// add id token for logout
currentIdentity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
I saw many examples where folks were populating the AuthenticationTicket inside the AuthorizationCodeReceived func delegate but for me it was always null. So some head scratching lead me to implementing what i needed inside the SecurityTokenValidated delegate. And it all works and hands together nicely.