Keycloak: have an Authenticator SPI "execution" available for clients flow - keycloak

I implemented a custom Authenticator to restrict login above a given number of existing sessions.
This Authenticator implementation doesn't use any user interaction nor require any model change.
public class MaxGroupAttributeValAuthenticator implements Authenticator {
#Override
public void action(AuthenticationFlowContext authenticationFlowContext) {
if toManySessions(authenticationFlowContext){
authenticationFlowContext.cancelLogin();
} else {
authenticationFlowContext.success();
}
}
It only check the current number of session
This Authenticator is available as an "execution" to be added to a copy of the default Browser Flow
But on a copy of the default Clients Flow, my Custom SPI is not available in the execution list.
How can I have my clients calling this Authenticator upon login/session_creation ?

Related

How to obtain a JWT token in Blazor code using OpenID Connect

Create a default Blazor server (not webassembly) application using windows identity platform as authorisation (I've used VS2022 / .net 6).
Is it possible to get hold of a JWT token in a code section of a blazor component (e.g. the LoginDisplay)?
For instance - I can get hold of the claims from the authentication state as follows (for my example in LoginDisplay.razor)
#code
{
[CascadingParameter] private Task<AuthenticationState> authenticationStateTask { get; set; }
protected override async Task OnInitializedAsync()
{
var authState = await authenticationStateTask;
var user = authState.User;
var identity = user.Identity as ClaimsIdentity;
if (identity != null)
{
IEnumerable<Claim> claims = identity.Claims;
// Can I get the a JWT Token signed by Azure B2C here?
}
}
}
Can I also get a JWT Token from the Azure service (I don't want to regenerate my own - I want an original one signed by microsoft).
As clarification - when using the MSAL javascript libraries on a different project (for a Single Page Application) I could do calls such as MSAL.acquireTokenSilent to get a token from the Azure B2C service for this purpose.
UPDATE - If HttpContext.GetTokenAsync returns null
In addition enets answer below. If you find that you can't access the JWT token using HttpContext.GetTokenAsync then see this question
You can access the access token and the refresh tokenas describe below, provided that you've set your app to use Jwt Token authentication (OpenID Connect). See this answer how to do that. Note: There is also a second answer related to that question by the same user. Search for it. This answer can also be useful. See this answer, which contains links to useful answers. Note: You can Google search string such as this: "enet stackoverflow blazor jwt token", and such like to find answers by me. If you want to see answers about the AuthenticationStateProvider, just search "enet stackoverflow blazor AuthenticationStateProvider"
Getting the access token
in _Host.cshtml you can code something like this:
#using Microsoft.AspNetCore.Authentication
#{
var tokens = new InitialApplicationState
{
AccessToken = await HttpContext.GetTokenAsync("access_token"),
RefreshToken = await HttpContext.GetTokenAsync("refresh_token")
};
}
And then pass the tokens object to your Blazor app like this:
<component type="typeof(App)" render-mode="ServerPrerendered" param-
InitialState="tokens"/>
Note that the tokens object is passed as a parameter to the App component, something like this:
#code{
[Parameter]
public InitialApplicationState InitialState { get; set; }
protected override Task OnInitializedAsync()
{
TokenProvider.AccessToken = InitialState.AccessToken;
TokenProvider.RefreshToken = InitialState.RefreshToken;
return base.OnInitializedAsync();
}
}
Note: TokenProvider is a singleton service instance that hold the JWT tokens, and make it available to other parts of your app. You can save the JWT tokens in the local storage or better the the session storage and read them when needed, etc.
Note: If you don't use Web Api, then you don't need Jwt token (authentication). Use Microsoft Identity authentication
To quote MS:
The built-in AuthenticationStateProvider service for Blazor Server apps obtains authentication state data from ASP.NET Core's HttpContext.User. This is how authentication state integrates with existing ASP.NET Core authentication mechanisms.
The Jwt token is in there as bearer.
To get the header there's a Question/Answer here by #enet that shows you how to access the HttpRequest from a Blazor Server App. - How to use the HttpContext object in server-side Blazor to retrieve information about the user, user agent.
I don't have a handy project which has JWT tokens to russle up some code for you. Someone else may be able to add another answer with code or add some to this.

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.

find onelogin login url

I want to implement onelogin concpet.
I have application APP-A1 and it holds a link of another application APP-A2
When user has logged in my app APP-A1, when user click on link of APP-A2*
It should not ask for login again.
I found below url
https://developers.onelogin.com/saml/c-and-aspnet
I assume When user click on link of APP-A2 the below code should run and and redirect to idp_sso_target_url
using OneLogin.Saml;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
AccountSettings accountSettings = new AccountSettings();
OneLogin.Saml.AuthRequest req = new AuthRequest(new AppSettings(),
accountSettings);
Response.Redirect(accountSettings.idp_sso_target_url + "?SAMLRequest=" +
Server.UrlEncode(req.GetRequest(AuthRequest.AuthRequestFormat.Base64)));
}
}
public class AccountSettings
{
public string certificate = "-----BEGIN CERTIFICATE-----\nMIIBrTCCAaGgAwIBAgIBATADBg
EAMGcxCzAJBgNVBAYTAlVTMRMwEQYDAQQIDApD\nYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2Ex
ETAPBgNVBAoMCE9uZUxv\nZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMB4XDTEwMDMwOTA5NTgzNF
oX\nDTE1MDMwATA5NTgzNFowZzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju\naWExFTATBgNVBA
cMDFNhbnRhIE1vbmljYTERMA8GA1UECgwIT25lTG9naW4xGTAX\nBgNVBAMMEGFwcC5vbmVsA2dpbi5Ab20w
gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJ\nAoGBANtmwriqGBbZy5Dwy2CmJEtHEENVPoATCZP3UDESRDQmXy
9Q0Kq1lBt+KyV4\nkJNHYAAQ9egLGWQ8/1atkPBye5s9fxROtf8VO3uk/x/X5VSROEIrhFISGmKUnVXa\nUh
LFIXkGSCAIVfoR5S2ggdfpINKUWGsWS/lEzLNYMBkURXuVAgMBAAEwAwYBAAMB\nAA==\n-----END CERTI
FICATE-----";
public string idp_sso_target_url = "https://app.onelogin.com/saml/signon/12345";
}
Below code base should be there on APP-A2 page to authenticate the incoming response and allow user to login the application.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// replace with an instance of the users account.
AccountSettings accountSettings = new AccountSettings();
OneLogin.Saml.Response samlResponse = new Response(accountSettings);
samlResponse.LoadXmlFromBase64(Request.Form["SAMLResponse"]);
if (samlResponse.IsValid())
{
Response.Write("OK!");
Response.Write(samlResponse.GetNameID());
}
else
{
Response.Write("Failed");
}
}
}
If my assumption is correct where should I get the URL in developer account,
Thanks
At SAML there are 2 actors:
- Identity Providers. Where the users are authenticated.
- Service Providers. Protect the apps and redirect to the IdP when user want to log in the app.
In your case, you need to add SAML support to APP-A1 and APP-A2 (turning them Service Providers) and connect both to an Identity Provider.
Service Peoviders need to send an AuthNRequest (first code you mentioned) to the IdP SSO URL.
And also need to have an Assertion Consumer Service (SP ACS URL, second code you mentioned) in order to receive and process the SAMLResponse.
If you are using Onelogin as Identity Provider and have a a developer account you will be able to go to Apps>Add app and later look for a "SAML Test Connector"
On the "Configuration" section you should provide SP info and at the SSO section you get the IdP data... you may create one App connector per each SP. More info at
https://support.onelogin.com/hc/en-us/articles/202673944-How-to-Use-the-OneLogin-SAML-Test-Connector
IMPORTANT: The dotnet toolkit is a proof of concept, you should not use it in production as mentioned on the description of its github repository:
https://github.com/onelogin/dotnet-saml
Please use the alternatives mentioned there.

Send trigger to onModule that RPC succeded

I have a Class that contain all the RPC.
One of them is :
public void authenticateUserCall(String username,String password ,DBConnectionAsync rpcService)
{
AsyncCallback<UserIdent> callback = new AsyncCallback<UserIdent>() {
public void onFailure(Throwable caught) {
Window.alert("Wrong Username or Password");
}
public void onSuccess(UserIdent result) {
Window.alert(result.getUserName());
}
};
rpcService.authenticateUser(username, password, callback);
}
If the RPC succeeds, I want to change the layout of the page to the main page for the user.
So how can I send to the onModule that the RPC succeeded ?
I don't want to build the layout in the onSuccess, and I can't clear the login layout because I don't have it on the onSuccess method.
What is the right way to do it ?
Option 1:
A couple of suggestions would be to pass in the AsyncCallback as an argument to the authenticateUserCall. This way the caller could handle on success.
Option 2: Recommended
Another option, which will give you much more flexibility is to use the EventBus to fire a custom AuthenticationEvent. I use this for handling things like roles. So for instance when the user is authenticated successfully it will return me some information about the user, like their username, password and role. I then use the EventBus to fire an AuthenticationEvent which contains this information about the user. Any of my views and activities can register for this event on the event bus and handle accordingly. So for example many of my views will disable functionality if the user is not an admin. When one of my activities handles this event it will grey out action buttons that require admin access.

Securing Babaganoush Web Api

Loving the Web API and have created some custom ones for our dynamic module. Is there a way we can secure all the web services rather than just the custom ones we have created?
Thanks.
When inheriting from BaseDynamicController<YouCustomModel>, you can override any of the services like this:
public override IEnumerable<YouCustomModel> Get(int take = 0)
{
if (/* Not Authenticated */)
throw new System.Security.SecurityException("Not authorized to access content");
return base.Get(take);
}
In a future release, we are looking to allow this to be controlled from a virtual method: https://babaganoush.uservoice.com/forums/259241-general/suggestions/6255143-secured-web-services-option-for-dynamic-content