when I access keycloak admin console (!remotely) and create client:
the keycloak OIDC JSON doesn't have public key
I would expect having in JSON something like:
"realm-public-key": "MIIBIjANBg....
keycloak.json in newest keycloak doesnot have any realm public key ... actually it appears that you are using keycloak version 2.3.x there have been some changes in it . Basically you can rotate multiple public keys for a realm . The document says this :-
In 2.3.0 release we added support for Public Key Rotation. When admin
rotates the realm keys in Keycloak admin console, the Client Adapter
will be able to recognize it and automatically download new public key
from Keycloak. However this automatic download of new keys is done
just if you don’t have realm-public-key option in your adapter with
the hardcoded public key. For this reason, we don’t recommend to use
realm-public-key option in adapter configuration anymore. Note this
option is still supported, but it may be useful just if you really
want to have hardcoded public key in your adapter configuration and
never download the public key from Keycloak. In theory, one reason for
this can be to avoid man-in-the-middle attack if you have untrusted
network between adapter and Keycloak, however in that case, it is much
better option to use HTTPS, which will secure all the requests between
adapter and Keycloak.
I still don't know why there is no public key in keycloak OIDC JSON (probably from security reason), but I have found it under:
Realm Settings > Keys > Public Key View
I had this same problem initially, but then figured out that I was using the wrong URL. The OpenID config is published by Keycloak under this URL, as per the standard:
http://localhost:8080/auth/realms/myrealm/.well-known/openid-configuration
But the public key is published under the jwks_uri, which is this:
http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/certs
Using that second URL, it is quite easy to validate the JWT using the connect2id library (https://connect2id.com/products/nimbus-jose-jwt/examples/validating-jwt-access-tokens):
...
JWT idToken = JWTParser.parse(bearerAccessToken.toString());
Nonce expectedNonce = null;
Issuer iss = new Issuer("http://localhost:8080/auth/realms/myrealm");
JWSAlgorithm jwsAlg = JWSAlgorithm.RS256;
URL jwkSetURL = new URL("http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/certs");
IDTokenValidator validator = new IDTokenValidator(iss, clientID, jwsAlg, jwkSetURL);
IDTokenClaimsSet claims = validator.validate(idToken, expectedNonce);
...
Related
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.
I am using Keycloak 11.0 and looking for an approach to get all the user federation providers associated to a given Realm. On my setup I have created a dummy Realm and added to it a dummy Federation.
I used the Keycloak Admin Rest API to get the Realm and then looking into the json userFederationProviders field, but nothing is there. I have also tried with the following Java code:
import org.keycloak.admin.client.Keycloak;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserFederationProviderRepresentation;
public class Key {
public static void main(String[] args){
Keycloak keycloak = Keycloak.getInstance(
"http://localhost:8080/auth/",
"dummyRealm",
"admin",
"a",
"admin-cli");
RealmRepresentation realm = keycloak.realm("dummyRealm").toRepresentation();
for(UserFederationProviderRepresentation s : realm.getUserFederationProviders())
System.out.println(s.getDisplayName());
}
}
However, I get a NPE because the realm.getUserFederationProviders() is null.
Is there any way to get the list of all userFederationProviders associated to a Realm without having to use the Keycloak Admin console?
List realm components and filter type=org.keycloak.storage.UserStorageProvider&parent=<realm name>. Check browser network console and you will see that this API request is also made by Keycloak Admin console, when you visit User Federation configuration.
I have access and a secret key to Google Cloud Storage and I want to instantiate a client using those credentials. I've been looking at tutorials and came across this example:
public class QuickstartSample {
public static void main(String... args) throws Exception {
// Instantiates a client
Storage storage = StorageOptions.getDefaultInstance().getService();
/* Perform some bucket action */
}
}
Is there any way I could pass that access and secret key while instantiating a client, in a fashion similar to that of AWS and Minio, something like this:
minioClient = new MinioClient("server URL", "accessKey",
"secretKey");
I'd really appreciate some help with this.
As you can see in this link, "you don't need to explicitly specify your credentials in code when using a Google Cloud Client Library".
There are though some other options to authenticate you client in your code, but none of them use access and secret key as it happens in AWS buckets, for instance. Here you have a number of options to implement that authentication in your code, such as using the JSON service account key within your code or using an OAuth2 access token.
I now have identityserver3 setup, i have 3 identityproviders configured:
- Local
- Google
- ADFS
I have multiple clients using Oidc-Client-JS (https://github.com/IdentityModel/oidc-client-js).
Now i would like to specify which identityprovider a client should use to login. so lets say:
Client A lets the user choose which provider to use
Client B logs in with local
Client C logs in with google
Client D logs in with ADFS
The situation of Client A is the default behavior and i have that working. My question is how do i set up clients B,C and D?
Check the following function in your start up see what you called your Identityprovider in my case "Google".
public static void ConfigureIdentityProviders(IAppBuilder app, string signInAsType)
{
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions
{
AuthenticationType = "Google",
In your client set the acr_value for idp to what ever you have set.
let userManagerSettings: Oidc.UserManagerSettings = {
acr_values: "idp:Google",
Now the client will automatically redirect to the correct identityprovider
According to the IdentityServer3 documentation, you need to configure the IdentityProviderRestrictions for each clients. In case of only one identity provider is configured, the IdSrv3 will automatically redirect.
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.