From Keycloak Login : How can I get the Bearer Token from KEYCLOAK_IDENTITY/KC_RESTART cookie - keycloak

I have a UI application running on ReactJS. For login we are redirecting it to keycloak login page.
After providing the username and password on keycloak login page,
I am seeing that KEYCLOAK_IDENTITY/KC_RESTART are set.
I have validated the KEYCLOAK_IDENTITY cookie at JWI.IO website as well. it is showing below details
{
"cid": "abc-client",
"pty": "openid-connect",
"ruri": "https://10.10.10.10:3001/",
"act": "AUTHENTICATE",
"notes": {
"scope": "openid",
"iss": "https://10.10.10.10:8445/auth/realms/abcrealm",
"response_type": "code",
"redirect_uri": "https://10.10.10.10:3001/",
"state": "879b6182-dca0-495c-8644-5b2ac032b5e4",
"nonce": "1e263181-2313-45ea-962a-175fd58c6d75",
"response_mode": "fragment"
}
}
Now My question is: how can I get the bearer token from this "KEYCLOAK_IDENTITY" cookie. I need the bearer token for all my next authorization checks on REST APIs because every user has different roles and can perform operations only if their profile has certain roles.
In other words, I just want to do a check/validate that provided KEYCLOAK_IDENTITY token is having a valid roles in the profile or not.

Use keycloak-js for validating the login. Initialise keycloak with keycloak config json. Make sure you are giving config of public client (No secret required). Once initialized, keycloak object will have both token and tokenParsed. It also has bunch of other useful functions. Check their docs.
import Keycloak from 'keycloak-js';
const keycloak = Keycloak('/keycloak.json');
keycloak.init({
onLoad: 'login-required',
}).then((authenticated) => {
if (authenticated) {
const token = keycloak.token;
const tokenParsed = keycloak.tokenParsed;
}
}

Related

How do I call my Cognito secured RestAPI from the browser when using next-auth?

I have an AWS RestApi secured by AWS Cognito. In addition to this I have a NextJS app using next-auth that provides user authentication against the Cognito User Pool.
I now want to call the RestApi directly from the browser, but cannot find a way to include the proper credentials. Since the only cookies present in my web-app begin with next-auth- I assume they are not suitable for the task.
So, how do I access the access token from the browser?
I ended up hooking up to two callbacks on the NextAuth configuration, like this:
NextAuth({
providers: [
CognitoProvider({
idToken: true,
issuer,
clientSecret,
clientId,
authorization,
}),
],
callbacks: {
session: async function ({ session, token }) {
return {
...session,
bearerToken: token.bearerToken ?? session.bearerToken,
};
},
async jwt({ token, account }) {
token.bearerToken = account?.id_token ?? token.bearerToken;
return token;
},
},
})
From the pages/api/auth/[...nextauth].ts file.
This makes the bearerToken available via the getSession call:
import { getSession } from "next-auth/react";
// ...
const { bearerToken } = await getSession();
You can use application like Postman to pass the Authorization header. I am not sure if your query is about getting the access token or about how to use the access token.
In order to get the access token and then to call your REST API, you need to have a server. Suppose you have your server with domain name "example.com". Then, put this server name in the callback URL of your Cognito user pool's app client.
After user authentication, Cognito will send the access token to "example.com". Then, you server will have the responsibility to correctly call the REST API with the access or ID token provided by Cognito.
For testing purpose, you can manually get the access token after authenticating with Cognito. Then, you can use application like Postman to make HTTP calls to your REST API endpoint with the access token. Put the token as value to the authorization header.

Keycloak only gets Google refresh token on first login

I'm using Keycloak with Google as identity provider. I need a refresh token from Google in order to manage the calendar of users. Here are my Keycloak Google IDP settings:
After login I fetch the refresh token according to https://www.keycloak.org/docs/latest/server_development/index.html#retrieving-external-idp-tokens. Which looks like this:
{
"access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"expires_in": 3599,
"refresh_expires_in": 0,
"refresh_token": "YYYYYYYYYYYYYYYYYYYYYYYYYYYY",
"token_type": "Bearer",
"id_token": "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
"not-before-policy": 0,
"scope": "openid https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
"accessTokenExpiration": 1593706596
}
Now the problem is when I login a second time and then try to fetch the refresh token again it's gone:
{
"access_token": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"expires_in": 3599,
"refresh_expires_in": 0,
"token_type": "Bearer",
"id_token": "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
"not-before-policy": 0,
"scope": "https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile openid",
"accessTokenExpiration": 1593706782
}
I'm really not sure how this is possible. One thing that came to my mind is that Keycloak is not respecting the "Request refresh token" setting on subsequest logins, but I don't know how to verify this.
It's not a Keycloak bug, it's a Google specification. The refresh_token is only provided on the first authorization from the user. Here the documentation of this behavior:
refresh_token: A token that you can use to obtain a new access token. Refresh tokens are valid until the user revokes access. Again, this field is only present in this response if you set the access_type parameter to offline in the initial request to Google's authorization server. - source
If you want to get the refresh token again you must:
Go to the page showing Apps with access to your account: https://myaccount.google.com/u/0/permissions.
Under the Third-party apps menu, choose your app.
Click Remove access and then click Ok to confirm
The next OAuth2 request you make will return a refresh_token (providing that it also includes the 'access_type=offline' query parameter.
related to this answer

How to send the Access Token in order to access AWS services - BEGINNER

I followed the following tutorial and got the facebook login working. At the end it prints out the accessToken which is great.
FB.login(function (response) {
// Check if the user logged in successfully.
if (response.authResponse) {
console.log('You are now logged in.');
// Add the Facebook access token to the Cognito credentials login map.
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: 'IDENTITY_POOL_ID',
Logins: {
'graph.facebook.com': response.authResponse.accessToken
}
});
// Obtain AWS credentials
AWS.config.credentials.get(function(){
// Access AWS resources here.
console.log('Access Token is '+ AWS.config.credentials.identityId);
var s3= new AWS.s3();
});
} else {
console.log('There was a problem logging you in.');
}
});
I believe that the following line of code Logins: {
'graph.facebook.com': response.authResponse.accessToken
} will register the accessToken in Cognito. Correct me if i am wrong.
My problem is, Should I save this accessToken and pass it when ever I am communicating with an AWS service (for example : DynamoDB write Item or else while adding a file to S3) ?
If so How can I do it ? (I have almost googled the entire web and found none on this)
** Note: I think the purpose of a accessToken is to add some security when the user request to access an AWS service. So in that case How can I use the accessToken to validate ?
Once you give it to Cognito in a call to get some credentials, Cognito will registered it to, and will give back credentials scoped to, that authenticated identity.
Those credentials alone will map back to that token, so all you need to give to other AWS clients when accessing them is the credentials. That being said, once those credentials have expired, you will need to have the token still handy to get more for that authenticated identity. Cognito requires at least one token linked to an authenticated identity to get credentials for it.

Non-Google Account Using chrome.identity

I'm trying to make a Gogle Chrome extension that requires user authorization to a SugarCRM 7.5 instance with OAuth 2.0 and I need to store the access token that's retrieved. I may need more clarification on how launchWebAuthFlow works.
Firstly, I can retrieve an access token from SugarCRM by using a POST request (not a GET request) that returns an access and a refresh token.
When I tried using the code below I kept getting the error: "authorization url can not be loaded" and when I checked the background console (I already know that my auth URL is wrong). Any help is appreciated even if you don't know SugarCRM. Just a general answer that can get me started is much appreciated.
manifest.json
{
"name": "Auth Sample",
"version": "1",
"manifest_version": 2,
"minimum_chrome_version": "29",
"key": "<long key>",
"app": {
"background": {
"scripts": ["background.js"]
}
},
"permissions": [
"identity",
"https://<sugar instance>/*"
]
}
Background.js
chrome.identity.launchWebAuthFlow(
{'url': '<url-to-do-auth>', 'interactive': true},
function(redirect_url) { console.log(redirect_url); });
My URL is definitely not gonna work here but it's something like this:
var client_id = '<client id from Oauth 2.0>';
var redirectUri = chrome.identity.getRedirectURL("sugarcrm");
var url = "https://<sugar instance base url>/rest/v10/oauth2/token?client_id=" + client_id + "&callbackURL=" + redirectUri + "&response_type=token"
What's the url-to-do-auth (is this the URL to do a regular login (username and password) to SugarCRM or is it a GET request through the REST API to do login)
I don't have a UI web auth flow for my SugarCRM instance. Is it possible to create one from the application or should that be on the server?
I can only make POST requests to get an access token. Will that still work with launchWebAuthFlow?
Update
I ended up storing the token using the local chrome.storage api and only stored the refresh token which will keep getting a new access token every time the application is run(I think it's more secure than just storing the access token as that will force it to always keep changing without passing other credentials)
After more search on launchWebAuthFlow. I found out that the url launchWebAuthFlow takes as parameter will launch a web page(with interactive parameter set to true) which is hosted on the server, that will let the user login and will return an access token if success. This url is actually an endpoint on the restful server. You need to create this endpoint that will be a get request with some parameters including a callbackURL, client_id and response_type. Then it will respond with another function that will be a post request and will take the username and password. If credentials are correct, it will return the access token as a parameter in the callbackURL(chrome extension specific url that contains the extension ID).
Please feel free to correct me or add something if I'm wrong.

How to verify Facebook access token?

There's only thing that server has to do; just check any access token's validity.
Clients send to the server user id and access token obtained by FB.getLoginStatus. As I expected, there would be any URL that checks access token's validity, like http://xxx.facebook.com/access_token?=xxxxxxxxxxxxxxxxxxxxxxxxxxxx.
That returns whether it's available one or not or is there any API (server side) for that?
The officially supported method for this is:
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
See the check token docs for more information.
An example response is:
{
"data": {
"app_id": 138483919580948,
"application": "Social Cafe",
"expires_at": 1352419328,
"is_valid": true,
"issued_at": 1347235328,
"metadata": {
"sso": "iphone-safari"
},
"scopes": [
"email",
"publish_actions"
],
"user_id": 1207059
}
}
You can simply request https://graph.facebook.com/me?access_token=xxxxxxxxxxxxxxxxx if you get an error, the token is invalid. If you get a JSON object with an id property then it is valid.
Unfortunately this will only tell you if your token is valid, not if it came from your app.
Just wanted to let you know that up until today I was first obtaining an app access token (via GET request to Facebook), and then using the received token as the app-token-or-admin-token in:
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
However, I just realized a better way of doing this (with the added benefit of requiring one less GET request):
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app_id}|{app_secret}
As described in Facebook's documentation for Access Tokens here.
Simply request (HTTP GET):
https://graph.facebook.com/USER_ID/access_token=xxxxxxxxxxxxxxxxx
That's it.
The app token can be found from this url.
https://developers.facebook.com/tools/accesstoken
I found this official tool from facebook developer page, this page will you following information related to access token - App ID, Type, App-Scoped,User last installed this app via, Issued, Expires, Data Access Expires, Valid, Origin, Scopes.
Just need access token.
https://developers.facebook.com/tools/debug/accesstoken/
Exchange Access Token for Mobile Number and Country Code (Server Side OR Client Side)
You can get the mobile number with your access_token with this API https://graph.accountkit.com/v1.1/me/?access_token=xxxxxxxxxxxx. Maybe, once you have the mobile number and the id, you can work with it to verify the user with your server & database.
xxxxxxxxxx above is the Access Token
Example Response :
{
"id": "61940819992708",
"phone": {
"number": "+91XX82923912",
"country_prefix": "91",
"national_number": "XX82923912"
}
}
Exchange Auth Code for Access Token (Server Side)
If you have an Auth Code instead, you can first get the Access Token with this API - https://graph.accountkit.com/v1.1/access_token?grant_type=authorization_code&code=xxxxxxxxxx&access_token=AA|yyyyyyyyyy|zzzzzzzzzz
xxxxxxxxxx, yyyyyyyyyy and zzzzzzzzzz above are the Auth Code, App ID and App Secret respectively.
Example Response
{
"id": "619XX819992708",
"access_token": "EMAWdcsi711meGS2qQpNk4XBTwUBIDtqYAKoZBbBZAEZCZAXyWVbqvKUyKgDZBniZBFwKVyoVGHXnquCcikBqc9ROF2qAxLRrqBYAvXknwND3dhHU0iLZCRwBNHNlyQZD",
"token_refresh_interval_sec": XX92000
}
Note - This is preferred on the server-side since the API requires the APP Secret which is not meant to be shared for security reasons.
Good Luck.