In Keycloak 12, this call returned the client secret inside the 'credentials' object:
keycloak.realm(tenantId)
.clients()
.get(applicationId)
.getInstallationProvider("keycloak-oidc-keycloak-json");
As of Keycloak 13, the same call no longer includes the 'secret' property in the 'credentials object:
https://issues.redhat.com/browse/KEYCLOAK-18257
Is there an alternative API call that will return the secret?
You can use Get the client secret request from Keycloak Admin API (of course with correct permissions properly configured):
GET /{realm}/clients/{id}/client-secret
https://www.keycloak.org/docs-api/14.0/rest-api/index.html
I guess you can define secret directly in the registration payload, so you won't to retrieve secret after.
Related
Below is my kubeconfig file for accessing kubernetes clusters:
kind: ClientConfig
apiVersion: authentication.gke.io/v2alpha1
spec:
name: dev-corp
server: https://10.x.x.x:443
certificateAuthorityData: ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
authentication:
- name: oidc
oidc:
clientID: aaaaad3-9aa1-33c8-dd0-ddddd6b5bf5
clientSecret: ccccccccccccccccc-
issuerURI: https://login.microsoftonline.com/aaaa92-aab7-bbfa-cccf-ddaaaaaaaa/v2.0
kubectlRedirectURI: http://localhost:12345/callback
cloudConsoleRedirectURI: http://console.cloud.google.com/kubernetes/oidc
scopes: offline_access,profile
userClaim: upn
userPrefix: '-'
groupsClaim: groups
preferredAuthentication: oidc
There are different OAuth grant types.
My understanding is, above OAuth grant type is client credential grant type, that requires client_id, client_secret, token URL(issuerURI), scope
What is the significance of fields kubectlRedirectURI, cloudConsoleRedirectURI, userClaim, userPrefix?
How OIDC different from OAuth2?
oauth2.Config does not store userClaim & userPrefix, groupsClaim information
, as shown here.... https://github.com/golang/oauth2/blob/master/oauth2.go#L41
How to store kind:ClientConfig with oidc based authentication into cache? for example api.Config can be stored with an API from client-go to write api.Config as shown here.
KUBECTL_REDIRECT_URL: the redirect URL that kubectl oidc login uses for authorization. This is typically of the format http://localhost:PORT/callback, where PORT is any port above 1024 that will be available on developer workstations, for example http://localhost:10000/callback. You must register the URL with your OIDC provider as an authorized redirect URL for the client application.
USER_PREFIX: prefix prepended to user claims to prevent conflicts with existing names. By default, an issuer prefix is appended to the userID given to the Kubernetes API server (unless the user claim is email). The resulting user identifier is ISSUER_URI#USER. We recommend using a prefix, but you can disable the prefix by setting USER_PREFIX to -.
userClaim: the user identifier in the token under the claim name configured in spec.authentication.oidc.userClaim in the client configuration file.
cloudConsoleRedirectURI the name tell the story, the cloud redirect URL for OIDC, for example in case of google https://console.cloud.google.com/kubernetes/oidc
OIDC vs OAuth2
What's the difference between OpenID and OAuth?
The file in the question from OIDC and you are comparing the value with Oauth, both handling at different way, better to update the question again with Oauth config file.
api-server-authentication
you can check kubeconfig builder
kubernetes-engine-oidc
How to store kind:ClientConfig with oidc based authentication into cache?
you can write to a file and then read, or somewhere in the cloud storage as well
I successfully receive an access token from the following call on keycloak:
http://localhost/auth/realms/myrealm/protocol/openid-connect/token
using
clent_id=myclient
grant_type=password
username=someone
password=mypasswd
client_secret=1a5debfc-63c8-48e8-95cb-b42aa0187310
I can use the token I get from this call on jwt.io, and it verifies correctly with the client secret. However, the following code always gives me an invalid signature error using the same info:
const token = "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJhOWY4NDhhYi05ZTllLTQ0ZDAtYWQ5NC1jN2VhMTBhMDMzOTIifQ.eyJleHAiOjE2Mzg1MDUwOTcsImlhdCI6MTYzODUwNDc5NywianRpIjoiMzYyZmJjNTgtMjM1Mi00YmM4LWE0NmUtMDliMGYzOTgzYzBhIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MC9hdXRoL3JlYWxtcy9teXJlYWxtIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjhlMTM1NTZlLTc2M2MtNGQxOC05OGFlLWY0ZWQ1YWFjODFiZSIsInR5cCI6IkJlYXJlciIsImF6cCI6Im15Y2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6ImYzMWUxM2NiLWUyZDgtNGIxZC04MTQzLTgxMmU3YmE0NTQ5NiIsImFjciI6IjEiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsiZGVmYXVsdC1yb2xlcy1teXJlYWxtIiwib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6ImYzMWUxM2NiLWUyZDgtNGIxZC04MTQzLTgxMmU3YmE0NTQ5NiIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJuYW1lIjoiTWlrZSIsInByZWZlcnJlZF91c2VybmFtZSI6InNvbWVvbmUiLCJnaXZlbl9uYW1lIjoiTWlrZSIsImVtYWlsIjoic29tZW9uZUBzb21lZmFrZWRvbWFpbi5jb20ifQ.Ir2qhdGqJzbJPn8S9TzDP2RRmN207pc8y3UrD7cCD5Q";
const secret = "1a5debfc-63c8-48e8-95cb-b42aa0187310";
jsonwebtoken.verify(token, secret, { "algorithms": ["HS256"] });
What am I missing?
I've just been through this myself recently using the jsonwebtoken npm package in a NestJS application. The following solved the problem:
Check the answer here -> https://stackoverflow.com/a/64484150 - this explains where to get the actual client secret, because it is not shown in the Keycloak interface anywhere, the secret can only be retrieved from the Keycloak database via that SQL command
Once you have that you need to change your verify call to be this:
jsonwebtoken.verify(token, Buffer.from(secret, 'base64'), { "algorithms": ["HS256"] });
This is needed because the client secret from the database is base64 encoded.
I used this approach to reduce calls to the Keycloak server's /auth/realms/{{realm-id}}/protocol/openid-connect/userinfo endpoint which adds network overhead, but as mentioned in your comment Mike, that REST API is a valid approach.
Background
On the Google Kubernetes Engine we've been using Cloud Endpoints, and the Extensible Service Proxy (v2) for service-to-service authentication.
The services authenticate themselves by including the bearer JWT token in the Authorization header of the HTTP requests.
The identity of the services has been maintained with GCP Service Accounts, and during deployment, the Json Service Account key is mounted to the container at a predefined location, and that location is set as the value of the GOOGLE_APPLICATION_CREDENTIALS env var.
The services are implemented in C# with ASP.NET Core, and to generate the actual JWT token, we use the Google Cloud SDK (https://github.com/googleapis/google-cloud-dotnet, and https://github.com/googleapis/google-api-dotnet-client), where we call the following method:
var credentials = GoogleCredential.GetApplicationDefault();
If the GOOGLE_APPLICATION_CREDENTIALS is correctly set to the path of the Service Account key, then this returns a ServiceAccountCredential object, on which we can call the GetAccessTokenForRequestAsync() method, which returns the actual JWT token.
var jwtToken = await credentials.GetAccessTokenForRequestAsync("https://other-service.example.com/");
var authHeader = $"Bearer {jwtToken}";
This process has been working correctly without any issues.
The situation is that we are in the process of migrating from using the manually maintained Service Account keys to using Workload Identity instead, and I cannot figure out how to correctly use the Google Cloud SDK to generate the necessary JWT tokens in this case.
The problem
When we enable Workload Identity in the container, and don't mount the Service Account key file, nor set the GOOGLE_APPLICATION_CREDENTIALS env var, then the GoogleCredential.GetApplicationDefault() call returns a ComputeCredential instead of a ServiceAccountCredential.
And if we call the GetAccessTokenForRequestAsync() method, that returns a token which is not in the JWT format.
I checked the implementation, and the token seems to be retrieved from the Metadata server, of which the expected response format seems to be the standard OAuth 2.0 model (represented in this model class):
{
"access_token": "foo",
"id_token": "bar",
"token_type": "Bearer",
...
}
And the GetAccessTokenForRequestAsync() method returns the value of access_token. But as far as I understand, that's not a JWT token, and indeed when I tried using it to authenticate against ESP, it responded with
{
"code": 16,
"message": "JWT validation failed: Bad JWT format: Invalid JSON in header",
..
}
As far as I understand, normally the id_token contains the JWT token, which should be accessible via the IdToken property of the TokenResponse object, which is also accessible via the SDK, I tried accessing it like this:
var jwtToken = ((ComputeCredential)creds.UnderlyingCredential).Token.IdToken;
But this returns null, so apparently the metadata server does not return anything in the id_token field.
Question
What would be the correct way to get the JWT token with the .NET Google Cloud SDK for accessing ESP, when using Workload Identity in GKE?
To get an IdToken for the attached service account, you can use GoogleCredential.GetApplicationDefault().GetOidcTokenAsync(...).
I'm using keycloak to get access tokens but I need those jwt tokens to have a 'policy' attribute/claim that MinIO requires.
Now, I can get those by calling the token endpoint with grant_type = password, plus username and pass.
I know that that policy attribute is mapped from the user, but, is there any possibility that I could get client creds (grant_type = client_credentials) including that attribute? or any other type of grant?
you can add Mapper to the client
Mapper Type: "hardcoded claim"
Token Claim Name: <token body key>, in your case is 'policy'
Claim value : <the value>
One of JWT benefits is to avoid managing/storing a session at the server side. However, in JWT the server needs to know the secret to corroborate that the incoming message is valid.
My question is: doesn't the server need to store the secret somewhere to validate the JWT token coming from the browser?
Yes, it does need to store the secret. If your server back-end is also issuing the tokens, you'll already have the secret used to sign the tokens available to verify the tokens. If you have some sort of auth server issuing the tokens, you'll need a shared secret to validate them.