get Id ( not clientId) from keycloak jwt token - keycloak

JWT only gives the user information and clientId, how can I get ID of the client as shown in the image
I CAN GET CLIENT DETAILS PARTICULARLY ID FROM THIS URL
https://${SERVER_URL}/auth/admin/realms/master/clients?clientId=test
API RESPONSE
{
"id": "12af651d-f6fe-49ef-8e6f-b951a49ff7db", // want to add this id into JWT
"clientId": "test",
"rootUrl": "${authBaseUrl}", ......
}
but I want to save my api call for getting Id
How can I get client information "Id" within jwt?
Now this is showing on the extraction of jwt
{
exp: 1627981808,
iat: 1627974608,
sub: '923666de-0b37-42f0-b694-0491028d0b78', // user Id
typ: 'Bearer',
azp: 'test', // client id
acr: '1',
scope: 'openid email profile',
email_verified: false,
name: 'name123',
preferred_username: 'name123',
given_name: 'name123',
email: 'any_name#gmail.com'
}
Any help/information will be helpful for me.

You should create and deploy a SPI to your keycloak instance. That's a JAR that overrides standard behaviour. There are various SPIs both described in the documentation and as code in GitHub keycloak repo.
With that SPI you can add just about anything to your token. I would suggest not to change anything that exists in the default token as this might impact your OID compatibility.
See more details here and here.

Related

Social Login using Google and Facebook in Flutter App

I am trying to implement my own server authentication (email, password) along with Facebook, and Google login. I would like to list what I have searched. Facebook login only returns an access token while Google login returns an access token and an id token.
I am planning to create my own JWT token in the backend, so which parameters (id token/access token) should be passed to my backend server to verify if the end user is logged in using Facebook or Google? I have some confusion about whether the access token is being used for authorization instead of authentication.
The token you get from google and facebook contain user data eg email, profile image etc you need to decode them.
Assuming email is unique in your db then you can query if exists and generate tokens on success.
example on how to decode google tokens in python
TLDR
from google.oauth2 import id_token
from google.auth.transport import requests
# (Receive token by HTTPS POST)
# ...
try:
# Specify the CLIENT_ID of the app that accesses the backend:
idinfo = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)
# Or, if multiple clients access the backend server:
# idinfo = id_token.verify_oauth2_token(token, requests.Request())
# if idinfo['aud'] not in [CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]:
# raise ValueError('Could not verify audience.')
# If auth request is from a G Suite domain:
# if idinfo['hd'] != GSUITE_DOMAIN_NAME:
# raise ValueError('Wrong hosted domain.')
# ID token is valid. Get the user's Google Account ID from the decoded token.
userid = idinfo['sub']
except ValueError:
# Invalid token
pass
Response
{
// These six fields are included in all Google ID Tokens.
"iss": "https://accounts.google.com",
"sub": "110169484474386276334",
"azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"iat": "1433978353",
"exp": "1433981953",
// These seven fields are only included when the user has granted the "profile" and
// "email" OAuth scopes to the application.
"email": "testuser#gmail.com",
"email_verified": "true",
"name" : "Test User",
"picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
"given_name": "Test",
"family_name": "User",
"locale": "en"
}

How do I load a "user" in a micronaut backend when JWT is provided

I have a Micronaut microservice that handles authentication via JsonWebTokens (JWT) from this guide.
Now I'd like to extend this code. The users in my app have some extra attributes such as email, adress, teamId etc. I have all users in the database.
How do I know in the backend controller method which user corresponds to the JWT that is sent by the client?
The guide contains this example code for the Micronaut REST controller:
#Secured(SecurityRule.IS_AUTHENTICATED)
#Controller
public class HomeController {
#Produces(MediaType.TEXT_PLAIN)
#Get
public String index(Principal principal) {
return principal.getName();
}
}
I know that I can get the name of the principal, ie. the username from the HttpRequest. But how do I get my additional attributes?
(Maybe I misunderstand JWT a bit???)
Are these JWT "claims" ?
Do I need to load the corresponding user by username from my DB table?
How can I verify that the sent username is actually valid?
edit Describing my usecase in more detail:
Security requirements of my use case
Do not expose valid information to the client
Validate everything the client (a mobile app) sends via REST
Authentication Flow
default oauth2 flow with JWTs:
Precondition: User is already registerd. Username, hash(password) and furhter attributes (email, adress, teamId, ..) are known on the backend.
Client POSTs username and password to /login endpoint
Client receives JWT in return, signed with server secret
On every future request the client sends this JWT as bearer in the Http header.
Backend validates JWT <==== this is what I want to know how to do this in Micronaut.
Questions
How to validate that the JWT is valid?
How to and where in which Java class should I fetch additional information for that user (the additional attributes). What ID should I use to fetch this information. The "sub" or "name" from the decoded JWT?
How do I load a “user” in a micronaut backend when JWT is provided?
I am reading this as you plan to load some kind of User object your database and access it in the controller.
If this is the case you need to hook into the place where Authentication instance is created to read the "sub" (username) of the token and then load it from the database.
How to extend authentication attributes with more details ?
By default for JWT authentication is created using JwtAuthenticationFactory and going more concrete default implementation is DefaultJwtAuthenticationFactory. If you plan to load more claims this could be done by replacing it and creating extended JWTClaimsSet or your own implementation of Authentication interface.
How do I access jwt claims ?
You need to check SecurityService -> getAuthentication() ->getAttributes(), it returns a map of security attributes which represent your token serialised as a map.
How to validate that the JWT is valid?
There is a basic validation rules checking the token is not expired and properly signed, all the rest validations especially for custom claims and validating agains a third parties sources have to be done on your own.
If you plan to validate your custom claims, I have already open source a project in this scope, please have a look.
https://github.com/traycho/micronaut-security-attributes
How to extend existing token with extra claims during its issuing ?
It is required to create your own claims generator extending JWTClaimsSetGenerator
#Singleton
#Replaces(JWTClaimsSetGenerator)
class CustomJWTClaimsSetGenerator extends JWTClaimsSetGenerator {
CustomJWTClaimsSetGenerator(TokenConfiguration tokenConfiguration, #Nullable JwtIdGenerator jwtIdGenerator, #Nullable ClaimsAudienceProvider claimsAudienceProvider) {
super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider)
}
protected void populateWithUserDetails(JWTClaimsSet.Builder builder, UserDetails userDetails) {
super.populateWithUserDetails(builder, userDetails)
// You your custom claims here
builder.claim('email', userDetails.getAttributes().get("email"));
}
}
How do I access jwt claims ?
If you want to access them from the rest handler just add io.micronaut.security.authentication.Authentication as an additional parameter in the handling method. Example
#Get("/{fooId}")
#Secured(SecurityRule.IS_AUTHENTICATED)
public HttpResponse<Foo> getFoo(long fooId, Authentication authentication) {
...
}
I found a solution. The UserDetails.attributes are serialized into the JWT. And they can easily be set in my CustomAuthenticationProviderclass:
#Singleton
#Slf4j
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Override
public Publisher<AuthenticationResponse> authenticate(
#Nullable HttpRequest<?> httpRequest,
AuthenticationRequest<?, ?> authenticationRequest)
{
// ... autenticate the request here ...
// eg. via BasicAuth or Oauth 2.0 OneTimeToken
// then if valid:
return Flowable.create(emitter -> {
UserDetails userDetails = new UserDetails("sherlock", Collections.emptyList(), "sherlock#micronaut.example");
// These attributes will be serialized as custom claims in the JWT
Map attrs = CollectionUtils.mapOf("email", email, "teamId", teamId)
userDetails.setAttributes(attrs);
emitter.onNext(userDetails);
emitter.onComplete();
}, BackpressureStrategy.ERROR);
}
}
And some more pitfalls when validating the JWT in the backend
A JWT in Micronaut MUST contain a "sub" claim. The JWT spec does not require this, but Micronaut does. The value of the "sub" claim will become the username of the created UserDetails object.
If you want to load addition attributes into these UserDetails when the JWT is validated in the backend, then you can do this by implementing a TokenValidator. But (another pitfal) then you must set its ORDER to a value larger than micronaut's JwtTokenValidator. Your order must be > 0 otherwise your TokenValidator will not be called at all.

Generate access token using JWT

I've been given access to an okta token endpoint. I would like to use this service to request a token. I was given a url, client id, client secret, scope and grant type. I can use postman to make a POST call to the url (/v1/token) and pass the above info (client id, client secret, scope and grant type) and I get an access token back.
I can easily make this call in java with RestTemplate or equivalent, but I would like to use an API that would manage the token for me.
I've found JJWT. All the examples I see out there show me how to create a JWT using JJWT. What I would like to do is to get my access token, but I'm not sure how to do that. I mean i get that JJWT is an API to create JWT, but then how can I use the JWT to get my access token?
Any help/clarification/direction is much appreciated.
We using JWT with the node.js, to create new Token jwt.sign(data, key) takes at least to an argument, the fist must be some credential like userId, email..., the second will be key to verify later. to verify the token is it valid we use jwt.verify(), the first argument is token (where the jwt.sing() give you) and the second is the key (where you provide when creating);
example:
Creating JWT token:
var jwt = require('jsonwebtoken');
cosnt token = jwt.sign({ email: 'test#test.com', userId: '993333' }, 'secretkey');
verifying Token:
try {
const decodedToken = jwt.verify(token, 'secretkey');
}
catch(err) {
throw new Error(err)
}
// once verified
conosole.log(decodedToken)
I found this post how to create and verify token using java, thanks!

Not able to set generated access_token in request object

I am authenticating user with the help of JWT token and #nestjs/passport library. I am able to successfully generate and return the access token but stuck at how to set it in the req object. So that I can validate other api routes until user logs out
Below code shows how I generated the access_token and returned to the calling point
let payload = { username: user.email, sub: user.id }
const accessToken = this.jwtService.sign(payload);
return {
expires_in: 3600,
access_token: accessToken,
status: 200
}
I am getting the access_token correctly. I verified generated access_token using https://jwt.io/ debugger and results were correct.
Can someone help me in setting up the req object with access_token?
There is multiple way you could send JWT token in your requests. If you have followed this tutorial: https://docs.nestjs.com/techniques/authentication, you are using passport-jwt library for JWT. One way is through header if you don't set auth schema it will be this one:
Authorization: JWT <-HERE YOU SHOULD INSERT TOKEN->
If you set authSchema to be bearer, then you could use this header like this:
Authorization: BEARER <-HERE YOU SHOULD INSERT TOKEN->
Except for that you can put your JWT into URL like this:
https://example.com/me?auth_token=<-HERE YOU SHOULD INSERT TOKEN->
Or in object body that you are sending with property named auth_token:
https://example.com/me
Body:
{
auth_token: <-HERE YOU SHOULD INSERT TOKEN->
}
You can find this in the source code of passport-jwt in this file.

using postman to access firebase REST API

I'm trying to use postman to do REST API calls to firebase. I've managed to read from firebase when my security rule is to permit all users including unauthorized ones.
but when I use this rule :
{"rules":{".read": "auth != null", ".write": "auth != null"}}
I get 'error' : 'permission denied' from postman.
I did the request token for google's web oauth2.0 client and got the authorization_code token back.
I tried to use token in the URL and in the header, tried it with GET & POST request and still get denied.
please help.
Thanks in advance
The answers above did not work for me.
What did work for me was going to
Project Settings (top left corner gear) -> Service Accounts (far right tab) -> Database Secrets (left menu) -> Scroll down, hover over the bulltets and click Show
Use this as the auth key, i.e. .../mycollection.json?auth=HERE
For me it worked like this:
https://your-database-url/users.json?auth=YOUR_AUTH_KEY
Where can you get this AUTH_KEY?
you get this key from your Project Settings -> Database -> Secret Key
Try something like this
https://your-database-url/users.json?auth=YOUR_AUTH_KEY
Respone is a JSON of your USERS node
I created a Postman pre-request script for helping create a Authentication: Bearer JWT. Should save a lot of copy pasting when testing APIs with Firebase Auth. https://gist.github.com/moneal/af2d988a770c3957df11e3360af62635
Copy of script at time of posting:
/**
* This script expects the global variables 'refresh_token' and 'firebase_api_key' to be set. 'firebase_api_key' can be found
* in the Firebase console under project settings then 'Web API Key'.
* 'refresh_token' as to be gathered from watching the network requests to https://securetoken.googleapis.com/v1/token from
* your Firebase app, look for the formdata values
*
* If all the data is found it makes a request to get a new token and sets a 'auth_jwt' environment variable and updates the
* global 'refresh_token'.
*
* Requests that need authentication should have a header with a key of 'Authentication' and value of '{{auth_jwt}}'
*
* Currently the nested assertions silently fail, I don't know why.
*/
pm.expect(pm.globals.has('refresh_token')).to.be.true;
pm.expect(pm.globals.has('firebase_api_key')).to.be.true;
var sdk = require('postman-collection'),
tokenRequest = new sdk.Request({
url: 'https://securetoken.googleapis.com/v1/token',
method: 'POST',
body: {
mode: 'urlencoded',
urlencoded: [{
type: 'text',
key: 'key',
value: pm.globals.get('firebase_api_key')
},
{
type: 'text',
key: 'grant_type',
value: 'refresh_token'
},
{
type: 'text',
key: 'refresh_token',
value: pm.globals.get('refresh_token')
},
]
}
});
pm.sendRequest(tokenRequest, function(err, response) {
pm.test('request for access token was ok', function() {
pm.expect(response).to.be.ok();
});
const json = response.json();
pm.expect(json).to.an('object');
pm.test('response json has needed properties', function() {
pm.expect(json).to.have.own.property('access_token');
pm.expect(json).to.have.own.property('token_type');
pm.expect(json).to.have.own.property('refresh_token');
const accessToken = json.access_token;
const tokenType = json.token_type;
const refreshToken = json.refresh_token;
pm.environment.set('auth_jwt', tokenType + ' ' + accessToken);
pm.globals.set('refresh_token', refreshToken);
});
});
Note: Adding this answer as all Options listed here is either deprecated or not working(mostly due to missing steps).
Best way to make it work with Postman is to use Google OAuth2 access tokens. The provided link described in full length but I have added quick steps.
Step 1: Download Service-Accounts.json
Step 2: Generate Access token in Java (provided link described support in other language for this)
make sure to include this dependency:
implementation 'com.google.api-client:google-api-client:1.25.0'
OR
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.25.0</version>
</dependency>
Run this code to generate token(Copied from google's javadocs)
// Load the service account key JSON file
FileInputStream serviceAccount = new FileInputStream("path/to/serviceAccountKey.json");
GoogleCredential scoped = GoogleCredential
.fromStream(serviceAccount)
.createScoped(
Arrays.asList(
"https://www.googleapis.com/auth/firebase.database",
"https://www.googleapis.com/auth/userinfo.email"
)
);
// Use the Google credential to generate an access token
scoped.refreshToken();
String token = scoped.getAccessToken();
System.out.println(token);
Step 3: Use the token in Postman
It is very simple to fetch data via Postman:
Here is how I did it
1 Your DB URL
https://YOUR_PROJECT_URL.firebaseio.com/YOUR_STRUCTURE/CLASS.json
2 Add API key in header as auth
auth = Value of your API_KEY
example:
We can use Firebase with postman for rest APIs
How we use Firebase in postman?
Copy your Firebase database URL
Paste it into your postman URL
Add .json at the last like shown in the image
Then play with your firebase database and make REST API