Identity Server - Identity/Resource scope - How can client get resource claims about the user - identityserver3

I have read Dominik's blog post on authentication vs permission modeling using Identity Server (https://leastprivilege.com/2016/12/16/identity-vs-permissions/). Since I am mostly using role based authorization, I am fine using IdentityServer as authentication/authorization endpoint for different client and apis.
My question is how to model Identity and Resource scopes properly? Can client know which roles are allowed for the user on specific resource? If yes, can client know only the user roles for requested resources scopes (and not all roles for all scopes).
As far as I understand the concept, if I request claims about the user via UserInfo endpoint, I am receiving claims which are filtered by claimTypes listed inside requested identity scopes. That means that if client requests roles scope (identity scope with role claimtype), UserInfo endpoint will respond with all role claims including other applications.
Let's take simple MVC example, where the MVC client communicates with API via REST.
MVC client (client) uses Cookie/OIDC Auth middleware and requests: ResponseType = "id_token token", Scope = "openid profile api".
The API (resource) uses IdentityServerBearerToken Auth middleware and demands: RequiredScopes = "api".
The client has UI elements which should be visible based on api role. How can the roles be accessed from client, since the UserInfo endpoint will only return identity scope based claims?
Should client ask the API (resource), which actions are possible? And based on the response show/hide UI elements?
Thank you for any help.

So things are different between IdentityServer3 and IdentityServer4. My example below is based on IdentityServer4.
You need to do a few of things:
define an IdentityResource that grants access to the role claim.
Include that new IdentityResource in the client's AllowedScopes
Have the client request your new IdentityResource as a scope.
So what I did was as follows:
Where I defined my resources:
new IdentityResource
{
Name = "roles",
UserClaims = { JwtClaimType.Role }
}
Defined my client as:
var client = new Client
{
// .. other stuff
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"roles"
}
}
And then used the the following in my javascript client:
new oidc.OidcClient({
// .. other stuff
scope: 'openid profile roles'
});
I was then able to see all the JwtClaimType.Role claims I'd added to the ClaimPrincipal in my javascript client.

You can put the roles in your id_token. Implement GetProfileDataAsync and add your roles when:
context.Caller == Constants.ProfileDataCallers.ClaimsProviderIdentityToken
or if you need them on the api you can also add them when:
context.Caller == onstants.ProfileDataCallers.ClaimsProviderAccessToken
If you make use of RequestedClaimTypes passed in the context you can filter out if a certain identity scope is requested or not.
For access tokens the resource scope matters (the claim types you specify on those scopes are put in the RequestedClaimTypes for all your requested scopes in the case of
context.Caller == Constants.ProfileDataCallers.ClaimsProviderAccessToken)
Both are called above if you ask for id_token token
The user info endpoint can be called by your api in case you use reference access token with openid scope (must be called otherwise you don't have any info) or want more claims than by default in the principal (coming from a jwt access token),
context.Caller == Constants.ProfileDataCallers.UserInfoEndpoint
(here the RequestedClaimTypes are the ones specified on the resource scopes your access token has access to..). You can also choose to include them in the id_token when the resource scope is part of the requested scopes, for example "openid myscope" where myscope is a resource (api for example), by enabling the 'AlwaysIncludeInIdToken' flag.

Fixed it, I needed to add the openid and profile scopes to the client;
AllowedScopes = new List<string>
{
"customAPI.read",
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile
}

Related

How to enable 2FA using email using keycloak-admin-client in spring boot

My requirement is enable 2FA using email in Keycloak.
When enabled, if user tries to login through email & password ,after user is successfully authenticated ,time based token will be sent to email .
User will do this action from custom UI i.e in our product we have UI to enable/disable 2FA for user.
We are using Keycloak & we want to achieve this using Keycloak API.
I am using keycloak-admin-client to interact with Keycloak API but I did not find sufficient resources to achieve this using keycloak-admin-client.
I am looking a way using keycloak-admin-client how to enable 2FA for user.
Any help will be highly appreciated.
Thank You
You should add custom REST endpoints to Keycloak to be able to enable 2FA from your custom UI. We have done this before. It's not that much complicated, but it requires you to have a look at Keycloak source to see what it's doing when OTP gets activated. Some important classes to check/use are TotpBean, OTPCredentialModel and OTPPolicy.
In order to enable the 2FA, we needed to show the QR code image in our custom UI. So we added an endpoint to Keycloak that instantiates an instance of TotpBean. It's the one that gives you access to the QR code image and the secret value that are required to generate the equivalent string representation of the image so that it could be scanned/entered in the 2FA app (e.g. Google Authenticator). Here is an example of how such an endpoint would look like:
#GET
#Produces({MediaType.APPLICATION_JSON})
#Path("/o2p-enable-config/{email}")
#NoCache
public Response fetchOtpEnableConfig(#Email #PathParam("email") String email) {
UserModel user = session.users().getUserByEmail(email, realm);
TotpBean totp = new TotpBean(session, realm, user, session.getContext().getUri().getRequestUriBuilder());
return Response
.ok(new YouOTPResponseClass("data:image/png;base64, " + totp.getTotpSecretQrCode(), totp.getTotpSecret(), totp.getTotpSecretEncoded()))
.build();
}
Then on your own backend, you call this endpoint and send the user's email to it and receive the image and the secret value. You can just display the image as is in your UI and keep the secret value on your backend (e.g. in user's session). When user scans the image using the app and enters the totp value provided by the app in your custom UI, you send the totp value and the secret to another endpoint that you should add to the Keycloak. This second endpoint is the one that does that verification of the value and enables 2FA.
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Path("/enable-2fa/{email}")
#NoCache
public Response enable2Fa(#Email #PathParam("email") String email, OtpDetails optDetails) {
OTPPolicy policy = realm.getOTPPolicy();
String totp = optDetails.getTotp();
UserModel user = session.users().getUserByEmail(email, realm);
OTPCredentialModel credential = OTPCredentialModel.createFromPolicy(realm, optDetails.getSecret(), optDetails.getUserLabel());
if (CredentialValidation.validOTP(totp, credential, policy.getLookAheadWindow())) {
CredentialHelper.createOTPCredential(session, realm, user, totp, credential);
return Response.noContent().status(204).build();
} else {
return Response.status(BAD_REQUEST).build();
}
}
Keycloak supports multiple 2FA for each user. That's why it also has a property named label that allows user to name them so that it would be displayed in the 2FA login scenario with given name. You can also allow user to enter the label value in your custom UI and pass it to the second endpoint (or just pass an empty value to Keycloak if you're not going to allow your users to setup multiple 2FA).
I know it seems complicated, but it's actually not that much. The Keycloak domain model is well designed and when you get familiar with it, you can easily find what you need to do and wrap it in custom APIs. But always ensure that exposing a functionality would not compromise the overall security model of the system.
Take a look at keycloak two factor email authenticator provider
https://github.com/mesutpiskin/keycloak-2fa-email-authenticator
I agree that is necessary to write a custom provider for this use case.
Take a look at https://www.n-k.de/2020/12/keycloak-2fa-sms-authentication.html and https://www.youtube.com/watch?v=GQi19817fFk for a look at how to implement that.
That is an example via SMS, but via e-mail would be very similar, changing just the way of sending the code to the user.

Get the user roles with the keycloak userinfo endpoint

How can I get the the roles included in the reply of the userinfo endpoint in keycloak. I defined a "Role Mapping" for the user in keycloak. When I call the userinfo endpoint I get the fields like email name etc, but the roles are not included in the reply. When I call the auth endpoint I get the access_token and in the field scope has roles included. Here is the reply from the auth endpoint:
access_token" QJsonValue(string, "eyJhb...")
"expires_in" QJsonValue(double, 300)
"not-before-policy" QJsonValue(double, 0)
"refresh_expires_in" QJsonValue(double, 1800)
"refresh_token" QJsonValue(string, "eyJhb...")
"scope" QJsonValue(string, "profile email roles")
"session_state" QJsonValue(string, "20b48536-4b38-4aa6-9072-e8309833402e")
"token_type" QJsonValue(string, "bearer")
I also tried to call the userinfo endpoint with the attribute "scope=roles", but this didn't work.
As someone already mentioned, it's a bug. I heard it's fixed in latest version of keycloak.
I eventually fixed with this setting without upgrading to the fixed version of keycloak.
When you add User Realm Role, it will have "realm_access.roles" as Token Claim Name. You need to change it to "roles". Then it will show correctly within userinfo.
Should be this issue: https://keycloak.discourse.group/t/resource-access-claim-missing-from-userinfo-until-i-change-the-name/1238
When renaming the claim in Client Scopes -> roles -> Mappers -> realm roles/client roles, i.e. realm_access.roles to realm_accessy.roles (and setting Add to userinfo to ON), it is included in userinfo :-/
In the mapper page on Keycloak, there is a setting called Add to userinfo, that has to be enabled.
For those whose above answer didn't work, I have spent the whole day figuring it out.
Basically, you have to go to client Scopes--> roles --> then move to Mappers tab, select client roles Add to Id token, access token and userinfo on
Here is the Screenshot

Access Keycloak group attributes from Nodejs

I've got Keycloak setup and running with NodeJS.
I see you can create groups and assign attributes to those groups. Is it possible to access these attributes from the NodeJS application?
I can't even find the groups let alone their attributes.
Yes you can. But there is almost no official documentation on how to achieve this. You can return most keycloak attributes, groups and roles through the client mappers. By default none are configured.
To configure extra mappers: In the administration console, select the client and then the Mappers tab. That should bring you to a list of mappers.
You can add mappers here of different types. Once you add a mapper you can decide which calls to Keycloak from the client return the attribute(s), and what the name of the returned attribute is. The following screenshot includes a mapper that returns a dictionary of groups, with subgroups, separated by forward slashes. Your Node code will need to parse the returned JSON object.
All the information is returned in the keycloak token, which is a Javascript Web Token. In Node you can examine it by printing the token to the log. The keycloak-connect middleware stores tokens etc in an object on the request called kauth. The path to retrieve a list of groups specified by the configuration in the above screenshot is shown below. If you change the token claim name in the configuration, you will need to change the path in your NodeJS code accordingly. You will need to logout from your application and login again for changes to the mapper to work.
router.get('/', async function(req, res){
console.log(req.kauth.grant.access_token.content.groups) ..
}

Manage AzureSearch via (Rest) API

I’m trying to create a tool which should scale the „Replicas“ and „Partitions“ of an Azure Search component.
For that, I read the following article from Microsoft:
https://learn.microsoft.com/en-us/rest/api/
Right now, I am having trouble authenticating against azure to get an AuthToken.
Is there a way to do it easier? Alternatively, do you guys have a sample in how to do it?
Here is a sample of my code:
var clientId = "2aaced54873e4a94b6d5518bc815dcb1";
var redirectUri = new Uri("https://thissucks.search.windows.net");
var resource = "resource"; // What exactly should the value be?
var authContext =
new AuthenticationContext(
"https://login.windows.net/ba1cb781739c4cdea71c619ccba914e0/oauth2/authorize", new TokenCache());
var result = authContext.AcquireTokenAsync(resource, clientId, redirectUri, new PlatformParameters(PromptBehavior.Auto));
var result2 = result.Result;
After invoking this, I get an Azure Login screen. After login with valid credentials, I get the following Exception:
System.AggregateException: 'One or more errors occurred.'
InnerException:
AdalServiceException: AADSTS50001: The application named was not found in the tenant named .
This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant.
You might have sent your authentication request to the wrong tenant.
So there're a few issues in your code.
First, please make sure that you have followed the steps described here: https://learn.microsoft.com/en-us/rest/api. Once the application is created successfully, you must note down the client id of that application and use that in your code.
Next, please ensure that ba1cb781739c4cdea71c619ccba914e0 is indeed the tenant id. You could also use Azure AD domain name (something.onmicrosoft.com) instead of this GUID type value. So your URL would be https://login.windows.net/something.onmicrosoft.com/oauth2/authorize
Lastly, there are issues with the values for the following parameters:
var redirectUri = new Uri("https://thissucks.search.windows.net");
var resource = "resource"; // What exactly should the value be?
redirectUri is the URI where the Azure AD will redirect once the user is successfully authenticated. For Web Applications it is usually the URL of your website. Please make sure that it matches with the value you provided when creating an application in Azure AD. When Azure AD redirects the user to this URL, it passes a JWT token in code query string parameter using which you get access/refresh token.
resource is the resource for which you're acquiring the token. Since you want to access Resource Manager API, the value here should be https://management.core.windows.net/.

IdentityServer3: How to assign ClientSecret to MVC Client?

I have configured IdentityServer3 with EF and AspNetIdentity. I have 3 MVC client applications. All users and clients are configured in SQL DB. I was able to get it working and users can now log in.
Now I'm trying to add some security around Client & Users and I have few questions related
1> Does MVC client only works with Implicit Flow? I have MVC client and below is it's OWIN startup.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Cookies"
});
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Authority = "https://localhost:44314/identity",
Scope = "openid",
ClientId = "LocalHostMvcClient",
RedirectUri = "http://localhost:34937/",
ResponseType = "id_token",
SignInAsAuthenticationType = "Cookies"
});
}
}
On IdentityServer, when I change the flow of LocalHostMvcClient to any flow (Other than 'Implicit') then client get error The client application is not known or is not authorized.. So it looks like for MVC client
it only works with Implicit flow.
Is this true?
2> Is ClientSecret not relevant for Implicit flow?
I want to use ClientSecret, but looks like it is not relevant for Implicit flow. Based on documentation ClientSecret is relevant to only flows that require secret. If answer to question 1 is true then does that mean i cannot use ClientSecret with Implicit Flow?
3> If i have multiple clients and users. Can we assign a user to a particular client?
For example, if i have 3 clients, www.client1.com, www.client2.com,www.client3.com and 2 users User1,User2. I want User1 to be able to login only for www.client1.com
Is this possible?
ASP.NET MVC can use any OpenID Connect flow. The error you are receiving is due to the client application requesting something it is not allowed to or otherwise being misconfigured in some way. Enable logging in Identity Server and it'll soon tell you why.
Client Secret is not used in Implicit, as implicit relies on the requesting url, not any sort of explicit authorization. That's why it's useful for client-side languages.
This is authorization logic and should be handled within the client application. For example when they login they would be shown an 'unauthorized' page. Identity Server is for authentication only.