Trouble with authenticating a test web app using keyrock - javascript

First we set up our app in the fiware lab:
the code that we are using to create the app is on this site
The only thing we changed from that link is the config.js:
var config = {}
config.idmURL = 'https://account.lab.fiware.org/';
config.client_id = 'f9b5940d67a741a38039690e4d6e6c6f';
config.client_secret = 'c9f854c96c9e4c70a0d402bce3233a17';
config.callbackURL = 'http://panonit.com:8802/user_info';
// Depending on Grant Type:
// Authorization Code Grant: code
// Implicit Grant: token
config.response_type = 'code';
module.exports = config;
When deploying the node server we have the following site up and running (on a colleagues laptop):
You can see it for yourself between the hours of 09h and 18h CET.
After we click log in we are properly taken to the fiware site where the user can authenticate:
And this is where the site breaks (it says page unavailable):
To over come this issue we only changed the server.js to output only the response:
// Ask IDM for user info
app.get('/user_info', function(req, res){
var url = config.idmURL + '/user/';
// Using the access token asks the IDM for the user info
oa.get(url, req.session.access_token, function (e, response) {
//var user = JSON.parse(response);
var user = response;
console.log("Getting user response is: " + user)
//res.send("Welcome " + user.displayName + "<br> Your email address is " + user.email + "<br><br><button onclick='window.location.href=\"/logout\"'>Log out</button>");
res.send("Welcome " + user)
});
});
After doing this we have restarted the server. From here we once again pressed the log in and authenticated the app usage and instead of the site break we get:
here we have concluded that the response is an empty object because undefined is printed out.
What are we doing wrong here?

Checking it, the problem is that you are using a wrong callback URL. If you check the server.js, the path for the callback URL you are using is /user_info, and to use that, first you need the req.session.access_token that you retrieve at /login. Just change the callback url for:
config.callbackURL = 'http://panonit.com:8802/login';
And everything should work. Also remember to change it in your IdM app configuration!

yes, the issue is what albertinisg has pointed out. The callbackURL must be /login in order to get the code and from it retrieve the access token. Then with the access token you will be able to retrieve the user info.
BR

Related

Integrate Salesforce registration page with VanillaJS oidc-client-js, getting the error - No matching state found in storage

Integrate Salesforce registration page with VanillaJS, getting the error - No matching state found in storage
We are redirecting the user to Salesforce registration page when Create Account button is created.
Once the User registers in Salesforce, the user is redirected to our site but we are getting this error. ('No matching state found in storage').
We tried the below solution but still getting the same error.
As I stated in my answer, the oidc client maintains state information
in the local storage so that it can verify that it got the response
back from the intended server. You can mimic this by generating a
secure random string and saving it in localStorage. Do this before
sending a request to your auth server to register a new user.
Reference- Integrate third party login in from my registration page with IdentityServer4 and Angular 6 - 'No matching state found in storage'
Is there a function related to creating registration? How to fix this issue?
Thanks.
Appreciate your help.
After spending days on this issue. Finally found the workaround as registration is not a feature of OIDC.
To overcome this issue, need to follow the Sign In process same as for Sign Up process, created the startSignupMainWindow method same as startSigninMainWindow and passing the signUpFlag:true as shown below in code.
/* This function is written to mimic the oidc library sign in process flow */
function startSignupMainWindow() {
var someState = {
message: window.location.href,
signUpFlag: true
};
mgr.signinRedirect({
state: someState,
useReplaceToNavigate: true
}).then(function() {
log("signinRedirect done");
}).catch(function(err) {
log(err);
});
}
Reading the signUpFlag:true in UserManager.js and swapping the Salesforce Sign In page Url with Sign Up page url and calling the Register function in Code.
UserManager.js(oidc - client - dev - js / src / UserManager.js)
//UserManager Customised Code :
return this.createSigninRequest(args).then(signinRequest => {
Log.debug("UserManager._signinStart: got signin request");
navigatorParams.url = signinRequest.url;
navigatorParams.id = signinRequest.state.id;
if (signinRequest.state._data.signUpFlag) {
register(signinRequest.state._id, signinRequest.state._code_challenge);
} else {
return handle.navigate(navigatorParams);
}
})
The below code is Register function written in code.
/* This function is written to send the code_challenge to salesforce server so that
salesforce server holds the code challenge and used to verify the further requests(token-request)
against the code_challenge it received initially.*/
//Customised register function written outside the library (Inside our App):
function register(_id, code_challenge) {
var date = new Date();
var baseUrl = "SALESFORCE_URL/login/SelfRegister?expid=id";
var expId = "id";
var userPage = encodeURIComponent(window.location.href);
var appDetails = "response_type=code&" +
"client_id=CLIENT_ID" +
"client_secret=CLIENT_SECRET&" +
"redirect_uri=CALLBACK_URL&" +
"state=" + _id + "&code_challenge=" + code_challenge + "&code_challenge_method=S256&response_mode=query";
var encodedapp = encodeURIComponent(appDetails);
var startUrl = "/services/oauth2/authorize/expid?" + encodedapp;
var signUpUrl = baseUrl + "&startURL=" + startUrl;
window.open(signUpUrl, "_self");
};

BigCommerce Hello World app - using app in the store

I installed successfully the Hello World app.
I'm trying to use the app in the storefront.
Following the instructions, I added the code provided to the script manager :
<script>
var appClientId = "**BC_CLIENT_ID**"; // TODO: Fill this in with your app's client ID.
var storeHash = "**TEST_STORE_HASH**"; // TODO: Fill this in wit the test store's store hash (found in base url before the `store-` part)
var appUrl = "**APP_URL**"; // TODO: Replace this with the URL to your app.
// Get the JWT token from the BC server signed first.
$.get('/customer/current.jwt?app_client_id=' + appClientId, function(jwtToken) {
// Now that we have the JWT token, use it to get the recent purchases block.
$.get(appUrl + '/storefront/' + storeHash + '/customers/' + jwtToken + '/recently_purchased.html', function(htmlContent) {
$('#recent_purchases_block').html(htmlContent, true);
});
});
</script>
I first ran into a "Uncaught ReferenceError: $ is not defined" error, which I solved including jquery-3.3.1.
But then, I get a 404 error with the GET request. I tried different values for APP_URL, but still get this error.
Here is the GET request :
https://www.marineff.com/rebates/load/storefront/STORE_HASH/customers/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJjdXN0b21lciI6eyJpZCI6MSwiZW1haWwiOiJqam9yaW9AaG90bWFpbC5jb20iLCJncm91cF9pZCI6IjAifSwiaXNzIjoiYmMvYXBwcyIsInN1YiI6Imt6N3Nma20yZHgiLCJpYXQiOjE1NTE5NDI5NTEsImV4cCI6MTU1MTk0Mzg1MSwidmVyc2lvbiI6MSwiYXVkIjoiNGZocWNvOTBha21qMzI5Z2lmMXY4dzFqdzRqN3FzZyIsImFwcGxpY2F0aW9uX2lkIjoiNGZocWNvOTBha21qMzI5Z2lmMXY4dzFqdzRqN3FzZyIsInN0b3JlX2hhc2giOiJrejdzZmttMmR4Iiwib3BlcmF0aW9uIjoiY3VycmVudF9jdXN0b21lciJ9.i7mguILu_jGue9nO5wZbtG8IhXqFkKuY71fAEsZTe27Tyxfjm4SAhUZUUxNvTbVhADP4Qm9kqY6BjU7I-SDgWA/recently_purchased.html
Any idea ?
Thanks

APNSwork on apn provider but not through nodeJS - Production

I have a nodeJS script set up to send APNs. When in development it always works but when i go to production they never get through. I tried taking the same notification Id it's sending and sending something using my production certificate in Easy Apn Provider and and it goes through. Im not sure why i could be failing. If my profile or certificates were wrong the easy apn wouldnt go through either?
apn config
var options = {
token: {
cert: "certificate.pem",
pfx: "Certificate.p12",
key: "AuthKey_XCVK62CSQF.p8",
keyId: "3Z6SEF7GE5",
teamId: "ASQJ3L7765"
},
production: true,
gateway: 'gateway.push.apple.com', // gateway address
port: 2195
};
var apnProvider = new apn.Provider(options);
Result of:
//IOS notif function
function SendIOSNotification(token, message, sound, payload, badge){
var deviceToken = token; //phone notification id
var notification = new apn.Notification(); //prepare notif
notification.topic = 'com.GL.Greek-Life'; // Specify your iOS app's Bundle ID (accessible within the project editor)
notification.expiry = Math.floor(Date.now() / 1000) + 3600; // Set expiration to 1 hour from now (in case device is offline)
notification.badge = badge; //selected badge
notification.sound = sound; //sound is configurable
notification.alert = message; //supports emoticon codes
notification.payload = {id: payload}; // Send any extra payload data with the notification which will be accessible to your app in didReceiveRemoteNotification
apnProvider.send(notification, deviceToken).then(function(result) { //send actual notifcation
// Check the result for any failed devices
var subToken = token.substring(0, 6);
console.log("Succesfully sent message to ", subToken);
}).catch( function (error) {
console.log("Faled to send message to ", subToken);
})
}
is successfully sent message to 5D..
Edit:
When displaying my response i see that the notification actually failed with a 403 error (id doesnt exist however it works just fine with easy apn).
I assume its because im generating a non production id but i dont understand how thats possible. I signed my build and have it on testflight and ive removed all signes of development profiles and only have production profiles and certificates. Not sure how thiis is happening
The issue was that when i changed to production and it wasnt working I tried creating a new .p8 key. It was likely working at that point but i only changed the reference to .p8 and not the key parameter to the key of the .p8 found on my developer account. Once i updated that key all my problems were solved

Getting the Auth Token for the secondary Id from Google chrome extension using OAuth 2.0 & Client ID

I am fairly new to developing chrome extensions, more specifically to the user authentication part in chrome extensions. I am following User Identity example from Google Developer docs.
The example works perfectly fine. I was able to generate the client id for the chrome app, add the scope for API's in my case Gmail API. And finally get the Auth Token by adding the identitypermission in manifest.json as follows
"oauth2": {
"client_id": "MY CLIENT ID",
"scopes": [
"https://www.googleapis.com/auth/gmail.readonly",
"https://www.googleapis.com/auth/gmail.modify"
]
}
And my app.js is a content_script which has the following code.
chrome.identity.getAuthToken({ 'interactive': true }, function(token) {
/* With which I can use xhr requests to get data from Gmail API */
console.log('Access Token : '+token);
});
Now this token that I get gives me the result for the user with which I have logged into chrome. Meaning Let's say I have a UserA with email address user_a#gmail.com and I have used this log into the chrome browser.
Question
How do I get the associated accounts or the secondary accounts? For instance, let's say a User Blogs into Gmail from the chrome browser. Is it possible to access the Gmail API for that particular user who is currently logged in?
I have tried a couple of things here.
gapi.auth.authorize({
'client_id': CLIENT_ID,
'scope': SCOPES.join(' '),
'immediate': true
},
function(authResult){//do something});
In the above scenario, the client id and scopes are fetched from the manifest.json using chrome.runtime.getManifest();.
This method uses the client.js from google api's and makes use of gapi variable.
In this case, I get the access token for the user whom I generated the client id, not even the chrome application user.
Furthermore, When I open an incognito mode and access this plugin, still I get the same user's access token.
Additional Note
I tried the same gapi.auth.authorize() using a Web OAuth 2 Client Id. It works perfectly fine. I mean whenever this authorize is executed it fetches the current logged in user's data or it asks for a login where the user can log in and authenticate. How do I achieve the same thing in chrome extension? Kindly let me know if I am missing something here.
As of now, this is not possible using supported APIs in Google Chrome stable (Version 63). However, in the Dev channel and most likely with a future release, the following will be possible:
chrome.identity.getAccounts(function(accounts) {
// accounts is a list of accounts.
chrome.identity.getAuthToken({ 'interactive': true, 'account': accounts[0] }, function(token) {
/* With which i can use xhr requests to get data from gmail api */
console.log('Access Token : '+token);
});
});
See the documentation for getAccounts().
EDIT: Something that might work in the meantime is registering for the onSigninChanged event.
How I ended up handling is was this (summary):
In the page layer, on load, I send a message down the stack to the background layer.
I used launchWebAuthFlow() to https://accounts.google.com/o/oauth2/auth to get the access_token for the account.
I made an AJAX call to https://www.googleapis.com/oauth2/v4/token using the access_token to get a refresh token.
When a user changes which account they are using via the avatar button on the top-right, this process is triggered again, as it is initiated by onLoad for the page layer of the extension.
The things left out the description above are caching and error handling, which are super-important.
http://developer.streak.com/2014/10/how-to-use-gmail-api-in-chrome-extension.html
is the complete solution which I have recently implemented on background page to work with Gmail API.
The content script is calling popup window to authorize using generated URL and simple server endpoint to store the refresh token.
$.oauthpopup = function (options) {
options.windowName = options.windowName || 'ConnectWithOAuth'; // should not include space for IE
var left = (screen.width / 2) - (800 / 2);
var top = (screen.height / 2) - (500 / 1.4);
options.windowOptions = options.windowOptions || 'location=0,status=0,width=800,height=500,left=' + left + ',top=' + top;
options.callback = options.callback || function () {
window.location.reload();
};
var that = this;
debug('oauthpopup open separate _oauthWindow');
that._oauthWindow = window.open(options.path, options.windowName, options.windowOptions);
};
$.oauthpopup({
path: 'https://accounts.google.com/o/oauth2/auth?' +
'access_type=offline' +
'&approval_prompt=force' +
'&client_id=' + clientID +
'&redirect_uri=' + callBackUrl +
'&response_type=code' +
'&scope=https://mail.google.com/ email profile' +
'&state=' + login.timyoUUID +
'&user_id=' + login.user_email,
callback: function () {
// do callback stuff
},
});
callBackUrl is used to store refresh token on the server.
Here is the example how I set access token for every request to gapi
export function setTokenForGAPI(accessToken) {
return getGAPIClient()
.then(() => {
const isSameToken = (currentAccessToken === accessToken);
const noToken = ((accessToken === undefined) || (accessToken === ''));
if (isSameToken || noToken) return;
gapi.auth.setToken({
access_token: accessToken,
});
currentAccessToken = accessToken;
})
.catch(function (e) {
console.log('error in setTokenForGAPI', e);
});
}
export function getEmailsByThreadIds(accessToken, ids) {
return setTokenForGAPI(accessToken)
.then(groupedThreadDetailsRequests(ids))
.then(processEmailDetailsResponse);
}

How to properly generate Facebook Graph API App Secret Proof in Javascript

I am making a server side Facebook Graph API call to the all_mutual_friends edge: https://developers.facebook.com/docs/graph-api/reference/user-context/all_mutual_friends/
The call works when the two users are friends, but returns no useful data when they users aren't friends. According to the docs, this is because I must sign the call with the appsecret_proof parameter. No matter what I try, I am not able to successfully pass this parameter. I am using jsrsasign running on Parse. I have tried every configuration of using the access token as the message and my appSecret as the key, and vice versa. I have also tried multiple combinations of utf8 and hex. Every time I receive the error: invalid appsecret_proof provided in the API argument
Code:
var Signer = require("cloud/vendor/jsrsasign/lib/jsrsasign.js");
var userId = request.params.userId;
var accessToken = request.params.accessToken;
var appSecret = "redactedStringPastedFromFacebook";
var signer = new Signer.Mac({alg: "hmacsha256", pass: appSecret});
var appSecretString = signer.doFinalString(accessToken);
var appSecretHex = signer.doFinalHex(accessToken);
var graphRequestURL = "https://graph.facebook.com/v2.5/" + userId;
var fields = "?fields=context.fields(all_mutual_friends.fields(name,picture.width(200).height(200)))";
//var authorization = "&access_token=" + accessToken; //this works, but only for existing friends
var authorization = "&access_token=" + accessToken + "&appsecret_proof=" + appSecretHex;
return Parse.Cloud.httpRequest({
url: graphRequestURL + fields + authorization,
method: "GET",
})
Most examples I have seen are in PHP or Python and the crypto routines are a bit more clear. This works in that both appSecretString and appSecretHex don't throw errors and look reasonable, however the values are always rejected by Facebook.
Notes:
I have triple checked the App Secret value provided by Facebook
I have been approved by Facebook to use the all_mutual_friends feature, which is a requirement for this particular call
I am using Parse, which isn't Node, and can't use NPM modules that have external dependencies, which is why I am using jsrsasign. I also tried using CryptoJS directly, but it is no longer maintained and doesn't have proper module support and jsrsasign seems to wrap it anyway.
Here it is:
import CryptoJS from 'crypto-js';
const accessToken = <your_page_access_token>;
const clientSecret = <your_app_client_secret>;
const appsecretProof = CryptoJS.HmacSHA256(accessToken, clientSecret).toString(CryptoJS.enc.Hex);

Categories