Signout with AFDS3 with SAML - saml

I have implemented SSO using ADFS3. I have logout button for sign out and it’s working fine with my ws-federation passive endpoints . In logout I redirect user to logout.aspx page and there I have written code on page load as
WSFederationAuthenticationModule authModule = FederatedAuthentication.WSFederationAuthenticationModule;
SignOutRequestMessage signOutRequestMessage = new SignOutRequestMessage(new Uri(authModule.Issuer), authModule.Realm);
String queryString = signOutRequestMessage.WriteQueryString();
Response.Redirect(queryString);
One of the application uses SAML so I have created SAML assertion consumer end point. So when I open this application and hit logout it throws error and when I see event log on ADFS
Encountered error during federation passive request.
Additional Data
Protocol Name:
wsfed
Relying Party:
Exception details:
Microsoft.IdentityServer.RequestFailedException: MSIS7055: Not all SAML session participants logged out properly. It is recommended to close your browser.
at Microsoft.IdentityServer.Web.Protocols.Saml.SamlProtocolHandler.BuildSamlLogoutResponse(SamlContext samlContext, Boolean partialLogout, Boolean& logoutComplete)
at Microsoft.IdentityServer.Web.Protocols.Saml.SamlProtocolHandler.ProcessSignOut(SamlContext samlContext, String redirectUri, List`1 iFrameUris, Boolean partialLogout)
at Microsoft.IdentityServer.Web.Protocols.Saml.SamlProtocolHandler.PipelineInitiatedSignout(WrappedHttpListenerContext httpContext, String redirectUri)
at Microsoft.IdentityServer.Web.PassiveProtocolListener.ProcessProtocolSignoutRequest(ProtocolContext protocolContext, PassiveProtocolHandler protocolHandler)
at Microsoft.IdentityServer.Web.PassiveProtocolListener.ProcessProtocolRequest(ProtocolContext protocolContext, PassiveProtocolHandler protocolHandler)
at Microsoft.IdentityServer.Web.PassiveProtocolListener.OnGetContext(WrappedHttpListenerContext context)

Related

C# owin implementation

I am implementing SP-initiated login for SAML authentication(with sustainsys saml library) with owin pipeline. I am facing an issue with receiving the saml response on the configured acs url. The saml response is received from IDP and user is successfully logged in, but when i try to read the saml response at the ACS url endpoint, that method is never hit in the debug flow.
I believe ACS endpoint is where the saml response will be sent back from idp(idp-browser and browser-acs endpoint), can someone point at the issue why saml response is received on the browser but not redirected to ACS URL.
Configured the ACS url on IDP and SP side.
i can see the correct ACS url in Saml request.
Sustainsys.Saml2.Owin.Saml2AuthenticationMiddleware Verbose: 0 : Signature validation passed for Saml Response Microsoft.IdentityModel.Tokens.Saml2.Saml2Id
Sustainsys.Saml2.Owin.Saml2AuthenticationMiddleware Verbose: 0 : Extracted SAML assertion <--Assertion_id-->
Sustainsys.Saml2.Owin.Saml2AuthenticationMiddleware Information: 0 : Successfully processed SAML response Microsoft.IdentityModel.Tokens.Saml2.Saml2Id and authenticated <--User-->
Application Insights Telemetry (unconfigured): ":,"ai.location.ip":"::1","ai.internal.sdkVersion"},"data":{"baseType":"RequestData","baseData":{"ver":2,"id":"|/WNNPHCMHVk=.56095c49_","name":"POST <--ACS URL- BASE URL-->","duration":"00:00:00.2807934","success":true,"responseCode":"303","url":"<--ACS URL-->","properties":{"DeveloperMode":"true","_MS.ProcessedByMetricExtractors":"(Name:'Requests', Ver:'1.0')"}}}}
The ACS endpoint is implemented by the library. And as seen in your log, it is done successfully.
Then the library issues a login using the configured login scheme, which is normally the external cookie scheme (but can also be the application cookie scheme). It's probably something there that's not correctly configured in your code.
I have set up the authentication type to ExternalCookie, it still doesn't reach my acs url with the saml response. POST request to acs url is returning with a 303 status code
In Startup
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieSecure = CookieSecureOption.Always,
ExpireTimeSpan = TimeSpan.FromMinutes(30),
LoginPath = new PathString("/Account/SignIn"),
SlidingExpiration = true,
});
app.SetDefaultSignInAsAuthenticationType(DefaultAuthenticationTypes.ExternalCookie);
app.UseSaml2Authentication(CreateSaml2Options());
public ActionResult ExternalLogin(string provider, string rURL, string uname)
{
return new ChallengeResult(provider,
Url.Action("ExternalLoginCallback",
"Account"), uname);
}
ACS ENDPOINT
[HttpPost]
public ActionResult Acs(string username, string samlResponse)
{
......
}
It will be very helpful if anyone can provide some input on this.

IdentityServer4 how to redirect the flow after login

I have installed an IdentityServer4 and a Client (Hybrid Mvc Client). All is ok. The following flow works:
1. User call secure page PageX (the controller is protected with Authorize attribute)
2. than system redirects the flow to Login page on IdentityServer
3. After authentication/authorization the IdentityServer redirect the user to url defined (redirect_uri) in the client configuration (page named Home) .
Now i don't know how to implement at the step 3 the redirection to PageX, the original page requested.
I have to create a custom AuthorizeAttribute to save on session storage the url of PageX and than using it in callback page? or is there any configuration on IdentityServer or client that could help me?
Thanks in advance
This is typically what you’d use the state parameter for. Your callback will receive the state value back unaltered and then you can verify the URL within is local and redirect to it automatically.
I’d recommend protecting the value from tampering using the DataProtection features in .net.
After successful login, by default the IdentityServer middleware tries to redirect to a consent page where to inform the user for the "allowed scopes". In this page are shown the claims that the client mvc site will receive access to: user identifier, user profile, email etc.
If you didn't setup such, you may set: "RequireConsent = false" when you define your MVC client. In such scenario the IdentityServer will redirect back to "RedirectUris" without showing consent page.
Example:
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId = "mvc",
ClientName = "mvc Client",
ClientSecrets =
{
new Secret("secret".Sha256())
},
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email
},
RequireConsent = false
}
};
}
The other thing that I've noticed in the IdentityServer4 demos and quick starts is that you need the following NuGet packages:
For client website:
IdentityModel,
Microsoft.AspNetCore.All
For IdentityServer Authentication app:
IdentityServer4,
IdentityServer4.AccessTokenValidation,
IdentityServer4.AspNetIdentity,
Microsoft.AspNetCore.All
You may install these packages just to get the demo working.

Keycloak - direct user link registration

I have set up a web application with Keycloak in my local machine. Since Im using Keycloak as SSO implementation, I want in my web app that whenever SIGNUP button is click, user is directed into the registration page, and not going through the LOGIN page.
This is the example URL directed to the registration form, however, it contains a tab_id that is generated randomly like a session id.
https://site.test/auth/realms/custom/login-actions/authenticate?client_id=test&tab_id=qIdW92Bvwmk
I read about this link
Yes, as long as you use the "registrations" instead of "auth" in the
end of login ( AuthorizationEndpoint ) URL
But my endpoint in https://site.test/auth/realms/custom/.well-known/openid-configuration cannot be modified.
You can change the button link to this format -
http://<domain.com>/auth/realms/<realm-name>/protocol/openid-connect/registrations?client_id=<client_id>&response_type=code&scope=openid email&redirect_uri=http://<domain.com>/<redirect-path>&kc_locale=<two-digit-lang-code>
The registration page is exposed via an openid-connect endpoint, accessible in the same way as the standard auth screen. To construct the correct URL you can simply replace openid-connect/auth in the URL with openid-connect/registrations from the .well-known auth endpoint.
authEndpoint.replace("openid-connect/auth","openid-connect/registrations");
Using this endpoint the user will be directed to the registration screen instead of the login screen.
It is not documented or exposed via .well-known/openid-configuration, but you can see it in the source code:
public static UriBuilder registrationsUrl(UriBuilder baseUriBuilder) {
UriBuilder uriBuilder = tokenServiceBaseUrl(baseUriBuilder);
return uriBuilder.path(OIDCLoginProtocolService.class, "registrations");
}
For Keycloak 17 this worked for me:
http://<mykeycloakdomain.com>/realms//protocol/openid-connect/registrations?client_id=<myclient_id>&response_type=code&scope=openid+email&redirect_uri=https%3A%2F%2Fmywebsiteurl.com&kc_locale=

IdentityServer SSO - Trusted application

I need to SSO (single sign on) a user coming from an application of mine (identity provider using ASPNET Session State) and redirect them to another application of mine (service provider) that is configured to use implicit flow with IdentityServer4. I need to achieve this without requiring the user to log back in and without providing the user's password.
My initial thought was that I could use a client secret for the identity provider to redirect the user to the IdentityServer4 authentication end point with the access token as a query parameter and then use a custom validator or extension grant to issue an identity token for use with the service provider application without needing to also provide the user's password.
I've managed to issue an access token to the identity provider and then redirect the user to IdentityServer4, but issuing an identity token has proven difficult for me. I've poured over the samples and documentation and I'm confused to say the least.
I'm looking for direction on the appropriate approach to this scenario and perhaps a comprehensive example in C#. I've come to understand I can use a hybrid flow to issue an access token as well as an identity token. I think my biggest struggle is how to redirect the user and, based on the access token, issue the user an identity token (and if this is even an acceptable approach).
Simply put: I'd like to redirect the user from Application A to IdentityServer4 to Application B based on trust with the identity provider (via client secret?).
Note: I understand this could be considered an opinion-based question, but based on my research I believe there is one single best practice and that's what I'm asking for.
I managed to get this working by the following flow:
Authorize the user in Application A (Identity Provider)
Obtain Access Token from Identity Server 4 via Token Endpoint and shared secret.
Add access token as a query string parameter since headers are not preserved on redirect.
Redirect the user to an Account controller method that accepts identifying information such as username. This method is protected by a custom middleware class that checks the query string for an access token parameter. If the token exists, it is added to the authentication header; this authorizes the user to hit this controller method.
The controller method will then sign the user in and redirect them to the /connect/authorize/login endpoint.
Finally, the login endpoint sets the cookie and redirects the user to Application B (Service Provider), whose URL is specified via the redirect_uri query parameter.
Configuration for shared secret:
Add appropriate grant type, secret and new scope name to the client. The new scope will help in debugging Access token issues in your logs (especially if you have multiple applications hitting your ID4 server). Also make sure to add the Service Provider's URL to the client RedirectUris, otherwise you'll receive an "invalid redirect" error.
AllowedGrantTypes = new List<string> { GrantType.Implicit, GrantType.ClientCredentials },
ClientSecrets = new List<Secret> {
new Secret(_clientSecrets.ExternalIdpSecret.Sha256(), clientID)
},
AllowedScopes = new List<string>
{
"newScopeName"
},
RedirectUris = new List<string>
{
$"http://localhost:<portnumber>"
}
Next, add your custom middleware.
public class QueryStringOAuthBearerMiddleware
{
private readonly RequestDelegate next;
public QueryStringOAuthBearerMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
this.BeginInvoke(context);
await this.next.Invoke(context);
this.EndInvoke(context);
}
private void BeginInvoke(HttpContext context)
{
if (context.Request.Query.ContainsKey("accesstokenparametername"))
{
var accessToken = context.Request.Query.First(p => p.Key == "accesstokenparametername");
if (!string.IsNullOrEmpty(accessToken.Value))
{
context.Request.Headers.Add("Authorization", "Bearer " + accessToken.Value);
}
}
}
private void EndInvoke(HttpContext context)
{
}
}
And add the middleware to your configuration.
app.UseMiddleware<QueryStringOAuthBearerMiddleware>();
Create your login method.
[HttpGet]
[Authorize]
public async Task<IActionResult> Login2(string userName, string returnURL)
{
await _httpContextWrapper.SignInAsync(userName);
return Redirect(returnURL);
}
Configuration for Client application (IDP):
Your client side code should look like this:
var disco = await DiscoveryClient.GetAsync("http://localhost:<portnumber>");
var tokenClient = new TokenClient(disco.TokenEndpoint, "clientIdentifier", "IUsedAGuidHere");
var tokenResponse = await tokenClient.RequestClientCredentialsAsync("newScopeName");
var redirectURL = string.Format("http://localhost:2228/account/Login2?userName=<UserIDValue>&returnURL={1}&accesstokenparametername={0}",
tokenResponse.AccessToken,
Server.UrlEncode(
string.Format("/connect/authorize/login?client_id={3}&redirect_uri={2}&response_type=id_token%20token&scope=<ImplicitFlowScopes>&state={0}&nonce={1}",
CryptoRandom.CreateUniqueId(),
CryptoRandom.CreateUniqueId(),
Server.UrlEncode("http://localhost:<PortNumber>"),
"ClientIdentifier")));
Response.Redirect(redirectURL, false);
Note: Please understand you won't be able to take this code AS-IS and make it work. I've heavily modified it to protect the security of my resources.
I think I might take care of the Authentication with Application A first, then forward on to the next app...
Application A --> IdentityServer --> Application A --> Application B.
You could include some custom parameters in your returnUrl which Application A could read upon return from IdentityServer that would trigger the redirect to Application B.

Identity Server 3, logout Js application from all tabs

I am using Identity Server 3 to authenticate a JS Client using the Implicit flow and AccessTokenType = AccessTokenType.Reference
But when i call
manager
.signoutRedirect()
.catch(function (error) {
console.error('error while logging out', error);
});
the user is signed out only from the calling tab, he can still browse the application in the other tabs.
Is it possible to log the user out from all the other tabs ?
I have already checked this question Oidc-client with IdentityServer3 - Angular2, how to logout and login properly
Make sure you have configured post_logout_redirect_uri in settings object. You can check if the user is logged out of application by checking the session_state value. In the case of JS application, the design of the specification requires to load, in a hidden iframe, the check session endpoint from IdentityServer. The JS application and this iframe can then communicate with the postMessage API.
You can find complete details here on IdentityServer3 documentation https://identityserver.github.io/Documentation/docsv2/overview/jsGettingStarted.html