Retrieve Custom Attribute from JavaScript (using Javascript Adapter) Keycloak - javascript

I have a keycloak user with custom attributes like below.
I use Reactjs for front-end. I want to retrieve the custom attribute from the javascript side. Like this answer states.
https://stackoverflow.com/a/32890003/2940265
But I can't find how to do it on the javascript side.
I debugged in Chrome but I can't find a suitable result for custom attributes.
Please help

I found the answer.
I will post here, because someone may find it useful.
Well, You can add custom attributes to the user but you need extra configurations to retrieve it from the javascript side. For Beginner ease, I will write the answer from Adding customer to retrieving the attribute from javascript (in my case react js).
Let's add custom attributes to a user.
login into keycloak and choose your realm (if you have multiple realms unless you will automatically login to realm)
After that select Users -> View all users
Select your user in my case it's Alice
Select Attributes and add custom attributes (in my case I added custom attribute call companyId like below)
Now click Save
Now we have to Map Custom attribute with our keycloak client.
To front end to use keycloak you must have client in Clients (left side bar)
If you haven't you have to configure a client for that. In my case my client is test-app
Therefor select Clients -> test-app -> Mappers
Now we have to create Mapper. Click Create
For Token Claim Name you should give your custom attributes key (in my case it is companyId) for my ease, I use companyId for Name, Realm Role prefix, Token Claim Name. You should choose User Attribute in Mapper Type and String for Claim JSON Type
After that click Save. Now you can get your custom attribute from javascript.
let say your keycloak JavaScript object is keycloak, you can get companyId using keycloak.
let companyId = keyCloak.idTokenParsed.companyId;
sample code would be like below (my code in react.js)
keyCloak.init({
onLoad: 'login-required'
}).success(authenticated => {
if (authenticated) {
if (hasIn(keyCloak.tokenParsed, 'realm_access')) {
if (keyCloak.tokenParsed.realm_access.roles === []) {
console.log("Error: No roles found in token")
} else {
let companyId = keyCloak.idTokenParsed.companyId;
}
} else {
console.log("Error: Cannot parse token");
}
} else {
console.log("Error: Authentication failed");
}
}).error(e => {
console.log("Error: " + e);
console.log(keyCloak);
});
Hope somebody find this answer useful, because I could find an answer for JavaScript. Happy coding :)

The attributes can be retrieved via the user profile:
keycloak = ... // Keycloak instance
keycloak.loadUserProfile().success(function(profile) {
let companyId = profile.attributes.companyId[0];
alert('Company Id: ' + companyId);
}).error(function() {
alert('Failed to load user profile');
});
Each attribute is an array of strings. So unless you have several company IDs, the array will have a length of 1 and the relevant data is in element 0.
In addition to custom attrbutes, the following elements are available as part of the user profile:
id
username
email
firstName
lastName
enabled
emailVerified
totp
createdTimestamp

Related

MSGraph API, filter mail by a Custom Value (internetMessageHeader)

Here is my goal :
Send an email through my interface with a custom value (Imagine, orderNumber186) then use it to filter all mails send or received by this value.
for example, I have a mail address with an icon, dans when I click on it I can see all discution with him, concerning the command (or whatever) that I'm on.
Exemple of a popup with mail info
If I'm on the order number 186, il click to the icon next to the mail and I see this popup with all mail received and send concerning this order precisely (Even the name or number is not mentionned, so not just a search query).
I consulted many documents from Microsoft as well as the forum, and this is all tests I carried out, with their advantages and problems :
internetMessageHeaders (example 2 form Microsoft doc, send mail)
With this solution, I can send a mail with my custom var easily, but it's impossible to get filtered mail with it, as it's said in this post.
Despite of it, I managed to filter myself, with foreach like this :
var listMail = [];
try {
//Look in all mails if they has an internetMessageHeaders with a name corresponding to var filterName
Providers.globalProvider.graph.client
.api("/me/mailFolders/SentItems/messages?$select=internetMessageHeaders")
.get((err, res) => {
if(err == null){
//Look if they are parameters
res.value.forEach(parameters => {
if(parameters.internetMessageHeaders != undefined){
//console.log(parameters);
//If Yes, loop for each internetMessageHeaders values to see if they have a corresponding name, then stock it inside a listMail array
parameters.internetMessageHeaders.forEach(element => {
if(element.name == filterName){
Providers.globalProvider.graph.client
.api("/me/messages/"+parameters.id)
.get((err, res) => {
listMail.push(res);
});
}
});
}
});
}
else {
console.log(err);
}
});
console.log('Email List => ', listMail)
}
catch (error) {
throw error;
}
So with this method, I can get all mail that contain internetMessageHeaders values.
Now, the problem :
For optimization, we definitely can't filter all mails to get mails that contain custom var, then fetch again to get the mail and store it in an handmade array, the best way is to do it with one query, to directly have all mails concerned.
For this, I've search about a second solution : singleValueLegacyExtendedProperty
I've found how to send mail with it, and even how to recover it.
When I use it, it work great when I fetch this request :
GET https://graph.microsoft.com/v1.0/me/mailFolders/SentItems/messages?$filter=singleValueExtendedProperties/any(ep:ep/id eq 'String {66f5a359-4659-4830-9070-00047ec6ac6e} Name MyName' and contains(ep/value, 'MyVar'))
My problem with this method, is that I can see all mail send, but if the client respond directly to the mail (By outlook for exemple), my var just disappear.
I think that it's not the case with x-var (internetMessageHeaders), but I'm stuck on it too.
So, my question is simple :
How to set a custom value to a mail, then filter all of it just by is custom value ?
Ideally, internetMessageHeaders is perfect, I just need to filter on it with a microsoft graph query directly.
Thank you for any help

Firebase custom claim how to set?

I'm struggling with firebase custom claims.
I have tested a lot of approaches nothing works. Obviously, I miss something important in the concept itself.
So I'm back to the root. This script from the google example should apply customs rule on a newly created user
exports.processSignUp = functions.auth.user().onCreate(event => {
const user = event.data; // The Firebase user.
const customClaims = {
param: true,
accessLevel: 9
};
// Set custom user claims on this newly created user.
return admin.auth().setCustomUserClaims(user.uid, customClaims)
});
Then on a client, I check the result with
firebase.auth().currentUser.getIdTokenResult()
.then((idTokenResult) => {
// Confirm the user is an Admin.
console.log(idTokenResult.claims)
if (!!idTokenResult.claims.param) {
// Show admin UI.
console.log("param")
} else {
// Show regular user UI.
console.log("no param")
}
})
.catch((error) => {
console.log(error);
});
Everything just a raw copy-paste still doesn't work. I've tested both from the local machine(there could be troubles with cors?) and deployed
This is a race situation. If the Function end first then, you will get the updated data.
The getIdTokenResult method does force refresh but if the custom claim is not ready then, it is pointless.
You need to set another data control structure to trigger the force refresh on the client. By example a real-time listener to the rtd;
root.child(`permissions/${uid}`).on..
And the logic inside the listener would be: if the value for that node exists and is a number greater than some threshold, then trigger the user auth refresh
During that time the ui can reflect a loading state if there is no datasnapshot or the not admin view if the datasnapshot exists but is a lower permission level.
In Functions you have to set the node after the claim is set:
..setCustomUserClaims(..).then(
ref.setValue(9)
);
I have a more detailed example on pastebin
The claims on the client are populated when the client gets an ID token from the server. The ID token is valid for an hour, after which the SDK automatically refreshes it.
By the time the Cloud Functions auth.user().onCreate gets called, the client has already gotten the ID token for the new user. This means that it can take up to an hour before the client sees the updated claims.
If you want the client to get the custom claims before that, you can force it to refresh the token. But in this video our security experts recommend (that you consider) using a different storage mechanism for claims that you want to be applied straight away.

storing user data in firebase with unique identifiers

I have a publicly accessible app. No sign in is required but I need a way to store user data in the database as an object associated with a unique key.
From what I understand, tokens would be a way to get a unique identifier from firebase(??)
I tried creating an anonymous user and getting a token like this:
let user = firebase.auth().signInAnonymously();
user.getIdToken(true);
I expected getIdToken to return a string but I get an object.
So...
1) Are tokens what I want to do this?
2) If so how can I get a new token as a string?
Use the following code as a global listener on ur page to check if the sign-in is successful:
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
var isAnonymous = user.isAnonymous;
var unique_id = user.uid;
} else {
// User is signed out.
// ...
}
});
This snippet has been taken from the Firebase Anonymous Auth link: Click Here to open link.
For some reason I was trying to set up a binding to sync my app with Firebase, which I just realized I don't need at all! (I just need to push the data at the end of the poll).
Of course as soon as removed that requirement it was as simple as:
firebase.database().ref().push().set(myData);
When using the push() method, Firebase automatically generates a unique key which is all I need...

Automatically assign a customer to a specific customer group on sign-up - Bigcommerce

I've been told by BC support that this isn't possible, but I would be surprised if there really wasn't a way.
I need to be able to automatically assign a customer to a specific customer group when they create an account. My thought:
I would add an extra field to the sign-up form
Provide a user with a code (a string or number)
User enters code when creating new account
User hits submit
On form submit I would grab the value of the extra field:
var codeInput = document.getElementById('code-input').value;
I would then compare that value to a pre-defined string, and if there is a match, I would assign that customer to groupX (with a group id of 8):
if ( codeInput === "codeIGaveToTheUser" ) {
currentUserGroupID = 8;
}
Is it possible to assign a customer to a specific group on sign-up like this (or any other way)?
Any help is much appreciated.
Although using BigCommerce webhooks would ensure the highest success rate of executing your customer group assignment app, it requires quite a bit of setup on BigCommerce (creating a draft app, getting an oAuth key, jumping jacks, etc), and may be a bit of overkill for your requirements.
Here's an easier way, in my {mostly} humble opinion, that takes advantage of much of what you included in your original question. Any solution though will nonetheless require an external server to handle the customer group assignment through the BigCommerce API.
Within the BigCommerce control panel, add in the extra field to the user sign up form like you mentioned.
So as you can see, this new input field has been added natively to the default registration page:
So now, when a user creates an account on your site, the value for the Signup Code (the custom field created) will be directly accessible through the API for that customer's account. Take a look at what that JSON data looks like:
Okay, so this is nice and all, but how do we automate it?
To do so, we will have to let our external application know that a customer just registered. Furthermore, our external application will need some sort of reference to this newly created customer, so that it knows which customer to update the customer group for. Normally a BigCommerce webhook would notify us of all this, but since we aren't using a BigCommerce webhook, here's the alternative method to triggering the external script.
We will trigger our external application via the BigCommerce Registration Confirmation page - createaccount_thanks.html. This page is loaded immediately after a customer creates an account, so it is the perfect place to insert our trigger script.
Additionally, now that the customer is logged in, we can access the customer's email address via a BigCommerce Global system variable -%%GLOBAL_CurrentCustomerEmail%%.
We should make an HTTP request from this page to our external application along with the customer's email address. Specifically, we can make an XMLHttpRequest via JavaScript, or to be modern, we'll use Ajax via jQuery. This script should be inserted before the closing </body> tag on createaccount_thanks.html.
Example of POST request (although a GET would suffice as well):
<script>
$(function() {
$('.TitleHeading').text('One moment, we are finalizing your account. Please wait.').next().hide(); // Let the customer know they should wait a second before leaving this page.
//** Configure and Execute the HTTP POST Request! **//
$.ajax({
url: 'the_url_to_your_script.com/script.php',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({email:"%%GLOBAL_CurrentCustomerEmail%%"}),
success: function() {
// If the customer group assignment goes well, display page and proceed normally. This callback is only called if your script returns a 200 status code.
$('.TitleHeading').text('%%LNG_CreateAccountThanks%%').next().show();
},
error: function() {
// If the customer group assignment failed, you might want to tell your customer to contact you. This callback is called if your script returns any status except 200.
$('.TitleHeading').text('There was a problem creating your account').after('Please contact us at +1-123-456-7890 so that we can look into the matter. Please feel free to continue shopping in the meantime.');
}
});
});
</script>
Now finally, you just need to create your serverside application responsible for handling the request above, and updating the customer's customer group. You can use any language that you desire, and BigCommerce even offers several SDK's you can use to save mega development time. Just remember that you need to host it somewhere online, and then insert its URL to the JS script above.
PHP Example (quick & dirty):
git clone https://github.com/bigcommerce/bigcommerce-api-php.git
curl -sS https://getcomposer.org/installer | php && php composer.phar install
<?php
/**
* StackOverflow/BigCommerce :: Set Customer Group Example
* http://stackoverflow.com/questions/37201106/
*
* Automatically assigning a customer group.
*/
//--------------MAIN------------------------//
// Load Dependencies:
require ('bigcommerce-api-php/vendor/autoload.php');
use Bigcommerce\Api\Client as bc;
// Define BigCommerce API Credentials:
define('BC_PATH', 'https://store-abc123.mybigcommerce.com');
define('BC_USER', 'user');
define('BC_PASS', 'token');
// Load & Parse the Email From the Request Body;
$email = json_decode(file_get_contents('php://input'))->email;
// Execute Script if API Connection Good & Email Set:
if ($email && setConnection()) {
$customer = bc::getCollection('/customers?email=' .$email)[0]; //Load customer by email
$cgid = determineCustomerGroup($customer->form_fields[0]->value); //Determine the relevant customer group ID, via your own set string comparisons.
bc::updateCustomer($customer->id, array('customer_group_id' => $cgid)) ? http_send_status(200) : http_send_status(500); //Update the customer group.
} else {
http_send_status(500);
exit;
}
//-------------------------------------------------//
/**
* Sets & tests the API connection.
* #return bool true if the connection successful.
*/
function setConnection() {
try {
bc::configure(array(
'store_url' => BC_PATH,
'username' => BC_USER,
'api_key' => BC_PASS
));
} catch (Exception $e) {
return false;
}
return bc::getResource('/time') ? true : false; //Test Connection
}
/**
* Hard define the customer group & signup code associations here.
* #param string The code user used at signup.
* #return int The associated customergroup ID.
*/
function determineCustomerGroup($signupCode) {
switch ($signupCode) {
case 'test123':
return 1;
case 'codeIGaveToTheUser':
return 8;
default:
return 0;
}
}
So then you would do your customer group string comparisons directly in the serverside program. I'd recommend you rewrite your own BC API script as the one above in quality is really something along the lines of functional pseudo-code, but more so present to show the general idea. HTH
You would need to set up a server to listen for webhooks unless you wanted to do a cron job. We have some basic information on the developer portal, but I included more resources below. From there, you'd need to choose your server language of choice to listen for the webhooks once they been created, respond correctly (200 response if received), execute code based on this information, and then take action against the BC API.
So if you were looking for a code, you'd need to listen for the store/customer/created webhook, and have your code look for a custom field that contained the code. If it was present, then take action. Else, do nothing.
https://developer.github.com/webhooks/configuring/
http://coconut.co/how-to-create-webhooks
How do I receive Github Webhooks in Python

meteor-useraccounts and alanning meteor-roles - Check role on sign-in

Is it possible to check users role on Sign in, and than if user is in role "admin" to display one page, and if is it in role "basic-user" to display another page ( go to another route).
Lets have a look at the Routes section of the documentation for the useraccounts:iron-routing package.
this should solve your problem
AccountsTemplates.configureRoute('signIn', {
redirect: function(){
var user = Meteor.user();
if (user && Roles.userIsInRole(user, ['admin'])) {
Router.go('admin');
}
else {
Router.go('home');
}
}
});
Be careful to check you can access the user roles field from the client side: lets check the allanning:roles official documentation
To define a default role for a user I use this:
// server
Accounts.onLogin(function(user) {
var user = user.user;
var defaultRole = ['student'];
if (!user.roles){
Roles.addUsersToRoles(user, defaultRole)
};
})
I'm using meteor-useraccounts and alanning meteor-roles packages and that work fine for me.
If I'm not outdated (and a look at http://docs.meteor.com/#/full/meteor_users suggests I'm not) there is no built in way for user roles. There should be some extensions for that task with which you could go and depending on what you choose you would have to check their documentation.
However it's not very hard to implement a own simple roles logic in Meteor:
First in your Accounts.onCreateUser function give your users object a new attribute role and assign them to a default role. If you don't have a Accounts.onCreateUser yet create one server side. It could look like something like this:
Accounts.onCreateUser(function(options, user) {
// Add an user roles array
user.roles = ["default-user"];
if (options.profile)
user.profile = options.profile;
return user;
}
Next you would need to implement some logic to add "admin" or whatever you like for trusted users to their roles array. That's up to you and for the beginning if you don't have dozens of admins you could also choose to do that manually in your MongoDB.
Now make sure you publish the new attribute of your user object to the currently logged in user. To do so make use of Meteor.publish with null as first parameter to address the current user like so:
Meteor.publish(null, function () {
return Meteor.users.find({_id: this.userId}, {fields: {
'roles': 1,
'profile': 1, // You probably want to publish the profile of a user to himself
// And here would be any other custom stuff you need
}});
});
And with that you are already in a state where you can do individual styling or routing client side. For example you could do something like this:
if (Meteor.user().roles.indexOf("admin") > -1) {
// Route for admins!
}
You could also parse through your array and add the user roles as class to your body element to for example only show certain elements to admins. This could be done this way:
Meteor.user().roles.forEach(function(role){
$('body').addClass(role);
});
Note that this will only be "cosmetic" but you can also implement real security with that as long as you do it server side. So if you want a Meteoer subscription or Meteor method only to be available for admins add something like this to it:
var requestingUser = Meteor.users.findOne({ '_id': this.userId});
if (!_.contains(requestingUser.roles, "admin")) {
// Terminate the pbulish function or Meteor method here when there is no "admin" role
return;
}
As said, this only works sever side and should be at the start of Meteor.publish functions or at the start of functions within Meteor.methods blocks.

Categories