Keycloak: how to get client_id where the user registered? - keycloak

I have a realm with several OpenId clients using SSO. I need to determine from which client each Keycloak user came from.
How can I get this information?

Answering my own question.
I didn't find natively this information in Keycloak's token. So I added a script.
In Authentication > Registration flow, I add an execution of type "Script" with the following function:
function authenticate(context) {
var username = user ? user.username : "anonymous";
var uri = context.getUriInfo();
LOG.info("setClientIdAttribute for URI " + context.getUriInfo().getRequestUri());
if (uri !== null) {
var clientId = uri.getQueryParameters().getFirst("client_id");
if (clientId !== null) {
LOG.info("Attribute 'origin' set with value " + clientId + " for user " + username);
user.setSingleAttribute('origin', clientId);
}
}
context.success();
}

If decoded each JWT has parameter azp, which is the client id.

If you decode the token, the field "aud" is your "client_id".
Check this information using jwt.io.

Related

Keycloak One Time Use token

We have a requirement to generate one time use tokens and use it in password reset emails.
Does Keycloak provide any standard API´s to generate and Validate one time tokens?.
We are using OIDC
Rsource Owner Password Credentials Grant (Direct Access Grants for authentication.
You can implement customized Action Token SPI, and Required Action SPI. Which would generate required token and then challenge user for them.
https://www.keycloak.org/docs/7.0/server_development/index.html#_action_token_spi
Anatomy of Action Token
nonce - Random nonce to guarantee uniqueness of use if the operation can only be executed once (optional). This is a random string that your application must generate.
Here’s an example of Java Servlet code that generates the URL to establish the account link.
​KeycloakSecurityContext session = (KeycloakSecurityContext) httpServletRequest.getAttribute(KeycloakSecurityContext.class.getName());
​AccessToken token = session.getToken();
​String clientId = token.getIssuedFor();
​String nonce = UUID.randomUUID().toString();
​MessageDigest md = null;
​try {
​md = MessageDigest.getInstance("SHA-256");
​} catch (NoSuchAlgorithmException e) {
​throw new RuntimeException(e);
​}
​String input = nonce + token.getSessionState() + clientId + provider;
​byte[] check = md.digest(input.getBytes(StandardCharsets.UTF_8));
​String hash = Base64Url.encode(check);
​request.getSession().setAttribute("hash", hash);
​String redirectUri = ...;
​String accountLinkUrl = KeycloakUriBuilder.fromUri(authServerRootUrl)
​.path("/auth/realms/{realm}/broker/{provider}/link")
​.queryParam("nonce", nonce)
​.queryParam("hash", hash)
​.queryParam("client_id", clientId)
​.queryParam("redirect_uri", redirectUri).build(realm, provider).toString();

How to get the Authentication Provider for actions-on-google on Node using account linking with Auth0?

I have Javascript App running under Node v8.11.2 which uses the Actions-On-Google library. I'm using the V2 API. I have account linking set up with Auth0 and am using the SignIn helper intent. Auth0 is set up to use Google, Facebook and Twitter.
The scopes I use are OPENID, OFFLINE_ACCESS, PROFILE and EMAIL.
Everything is working fine and when the User is authenticated I get an Access Token returned.
My question is, how do I get the Authentication Provider that was selected by the User so that I can use the Access Token correctly to retrieve profile elements such as the display name, email address etc??
The signin object passed to the Sign In Confirmation intent handler just contains the following regardless of the provider selected: -
{"#type":"type.googleapis.com/google.actions.v2.SignInValue","status":"OK"}
Any help greatly appreciated as I have a deadline and this is driving me a bit crazy now!
Thanks,
Shaun
If your question is about how to get the required information when you have your accessToken available then you could use what is shown in this answer.
In node this looks like that:
let link = "https://www.googleapis.com/oauth2/v1/userinfo?access_token="+accessToken;
return new Promise(resolve => {
request(link,(error, response, body) => {
if (!error && response.statusCode === 200) {
let data = JSON.parse(body);
let name = data.given_name ? data.given_name : '';
conv.ask(new SimpleResponse({
speech: "Hello "+ name + "!",
text: "Hello "+ name + "!"
}));
resolve();
} else {
console.log("Error in request promise: "+error);
resolve();
}
})
})
Everything you need should be in the data object.
Hope it helps.

IdentityServer4 implicit grant acr values

I am trying to pass tenant id as parameter to identityserver4 implicit grant end point. The client is written using angularjs, are there any examples to pass the tenantid from angular app to identityserver4 end point.
I have found that this feature was implemented using acr_values. More details are here - https://github.com/IdentityServer/IdentityServer3/issues/348
yes of course. you can always pass userID or any other variable in the state variable of your AUth request. if you include userID as a parameter in your request URL when sending it to Auth server, it'll return the state to redirect URI. which means you'll have access to it there. the issue is when an implicit request is being sent, it practically detaches your app from server cause the response URI (possibly) is in a different state/page. so by passing it as in state, the authorization server includes this value when redirecting the user-agent back to the client.
public Authenticationrequest() {
var client_id = "YOUR-CLIENT-ID";
var scope = "OPTIONAL";
var redirect_uri = "YOUR_REDIRECT_URI";
var response_type = "token";
var authserver = "YOUR-AUTH-SERVER-URL?";
var state = "OPTIONAL"; // put UserID here
var AuthenticationURL = authserver + "response_type=" + response_type + "&scope=" + scope + "&client_id=" + client_id + "&state=" + state + "&redirect_uri=" + redirect_uri;
return AuthenticationURL;
};

can't get gmail user displayname via firebase3 login in Ionic platform

I am working on google auth with firebase signinwithcredential. I first used cordovaoath to get the id token and sign it in with firebase. I specifically indicated in my code that I would like to get the displayName, but it always come back null. I was wondering anyone have faced the same problem?
this.loginWithGoogle = function loginWithGoogle() {
$cordovaOauth.google("mygoogleclient id here", ["https://www.googleapis.com/auth/calendar","https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile","https://www.googleapis.com/auth/plus.me", "https://www.googleapis.com/auth/plus.login", "https://www.googleapis.com/auth/drive"]).then(function (result) {
// "email", "profile",
console.log("first come here to oath");
console.log("Response Object -> " + JSON.stringify(result));
var unsubscribe = firebase.auth().onAuthStateChanged(function (firebaseUser) {
unsubscribe();
// Check if we are already signed-in Firebase with the correct user.
// Build Firebase credential with the Google ID token.
var credential='';
credential = firebase.auth.GoogleAuthProvider.credential(
result.id_token);
// Sign in with credential from the Google user.
firebase.auth().signInWithCredential(credential).catch(function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
});
});
}, function (error) {
console.log("Error -> " + error);
});
};
sreenshot of my returned result
You should request an access token, and not an ID token, if you are requesting additional scopes.
To fix your code, change this line:
var credential = firebase.auth.GoogleAuthProvider.credential(
null, result.access_token); // <- access_token

Changing user folder collaborating type in box using Salesforce Toolbox

I'm trying to change Box folder collaboration type for user from salesforce Apex trigger. The first thoughts were to use box.Toolkit but it looks like this class does not have updateCollaboration or changeCollaboration method, only create. I guess my only option is to use Box's Rest API. Is there any way I can get service account token in Apex so I can use it in a callout?
I have created a special "Tokens" object in Salesforce with two fields: access token and refresh token. I then have a batch job that runs to update the access token every 55 minutes such that they never expired.
Here is a code snippet in APEX using the Tokens object.
#future(callout=true)
public static void updateTokens(){
//app info for authenticating
String clientID = 'MY_CLIENT_ID';
String clientSecret = 'MY_CLIENT_SECRET';
//look up value of existing refresh token
Token__c myToken = [SELECT Name, Value__c FROM Token__c WHERE Name='Refresh'];
Token__c myAccessToken = [SELECT Name, Value__c FROM Token__c WHERE Name='Access'];
String refreshToken = myToken.Value__c;
String accessToken = myAccessToken.Value__c;
//variables for storing data
String BoxJSON = '';
String debugTxt = '';
//callout to Box API to get new tokens
HttpRequest reqRefresh = new HttpRequest();
reqRefresh.setMethod('POST');
String endpointRefresh = 'https://www.box.com/api/oauth2/token';
reqRefresh.setEndpoint(endpointRefresh);
String requestBody = ('grant_type=refresh_token&refresh_token=' + refreshToken + '&client_id=' + clientID + '&client_secret=' + clientSecret);
reqRefresh.setBody(requestBody);
System.debug('Body of refresh request: ' + requestBody);
//Create Http, send request
Http httpRefresh = new Http();
Boolean successRefresh = false;
while (successRefresh == false){
try{
HTTPResponse resRefresh = httpRefresh.send(reqRefresh);
BoxJSON = resRefresh.getBody();
System.debug('Body of refresh response: ' + BoxJSON);
successRefresh = true;
}
catch (System.Exception e){
System.debug('Error refreshing: ' + string.valueof(e));
if (Test.isRunningTest()){
successRefresh = true;
}
}
}
Keep in mind that if you are using the Box for Salesforce integration your administrator can set the option for the permissions on the folders to sync with Salesforce permissions. This would reverse any changes you make to collaborations. Check out more about Box's Salesforce integration permissions here: https://support.box.com/hc/en-us/articles/202509066-Box-for-Salesforce-Administration#BfS_admin_perm