Simple MSAL Login/Authentication in JavaScript - javascript

I'm trying to do a simple login to Azure AD using the MSAL for JavaScript v2.0 library. We want users to be able to authenticate into our site with their work Microsoft accounts. All I need to do is be able to authenticate/login the user via Microsoft, and if they can login via their work Microsoft account, then they're granted access to our site.
I'm using the Javascript library and have followed the code from the Github page and while the login prompt is coming up, afterwards I have no idea how to check if the user is signed in.
Here's the code I'm using, which is basically what's in the sample code from Github:
<script type="text/javascript" src="https://alcdn.msauth.net/browser/2.15.0/js/msal-browser.min.js"></script>
<script type="text/javascript">
const msalConfig = {
auth: {
clientId: "[ClientID goes here]",
authority: "https://login.microsoftonline.com/[tenant ID]",
knownAuthorities: ["login.microsoftonline.com"],
protocolMode: "OIDC",
redirectUri: "[page on our site that doesn't have MSAL auth, listed in Azure Reply URLs]"
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: true, // Set this to "true" if you are having issues on IE11 or Edge
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case msal.LogLevel.Error:
console.error(message);
return;
case msal.LogLevel.Info:
console.info(message);
return;
case msal.LogLevel.Verbose:
console.debug(message);
return;
case msal.LogLevel.Warning:
console.warn(message);
return;
}
}
}
}
};
// Add here scopes for id token to be used at MS Identity Platform endpoints.
const loginRequest = {
scopes: ["User.Read"]
};
const silentRequest = {
scopes: ["openid", "profile", "User.Read"]
};
const ua = window.navigator.userAgent;
const msie = ua.indexOf("MSIE ");
const msie11 = ua.indexOf("Trident/");
const msedge = ua.indexOf("Edge/");
const isIE = msie > 0 || msie11 > 0;
const isEdge = msedge > 0;
let signInType;
let accountId = "";
let credType = "";
// Create the main myMSALObj instance
const myMSALObj = new msal.PublicClientApplication(msalConfig);
// Register Callbacks for Redirect flow
myMSALObj.handleRedirectPromise().then(handleResponse).catch((error) => {
console.log(error);
});
function handleResponse(resp) {
alert("beginning handleResponse");
if (resp !== null) {
accountId = resp.account.homeAccountId;
credType = resp.account.credentialType;
myMSALObj.setActiveAccount(resp.account);
alert("response not null (already auth), accountId: " + accountId + ", credType: " + credType);
}
else {
const currentAccounts = myMSALObj.getAllAccounts();
if (!currentAccounts || currentAccounts.length < 1) {
alert("currentAccounts null/empty, going to signIn");
signIn("loginRedirect");
//return;
}
else if (currentAccounts.length > 1) {
// add choose account code here
alert("currentAccounts has multiple");
}
else if (currentAccounts.length === 1) {
const activeAccount = currentAccounts[0];
myMSALObj.setActiveAccount(activeAccount);
accountId = activeAccount.homeAccountId;
credType = activeAccount.credentialType;
alert("currentAccounts == 1; accountId: " + accountId + ", credType: " + credType);
}
}
}
async function signIn(method) {
signInType = isIE ? "loginRedirect" : method;
if (signInType === "loginPopup") {
return myMSALObj.loginPopup(loginRequest).then(handleResponse).catch(function (error) {
console.log(error);
});
}
else if (signInType === "loginRedirect") {
return myMSALObj.loginRedirect(loginRequest);
}
}
function signOut() {
const logoutRequest = {
account: myMSALObj.getAccountByHomeId(accountId)
};
myMSALObj.logoutRedirect(logoutRequest);
}
async function getTokenPopup(request, account) {
request.account = account;
return await myMSALObj.acquireTokenSilent(request).catch(async (error) => {
console.log("silent token acquisition fails.");
if (error instanceof msal.InteractionRequiredAuthError) {
console.log("acquiring token using popup");
return myMSALObj.acquireTokenPopup(request).catch(error => {
console.error(error);
});
}
else {
console.error(error);
}
});
}
// This function can be removed if you do not need to support IE
async function getTokenRedirect(request, account) {
request.account = account;
return await myMSALObj.acquireTokenSilent(request).catch(async (error) => {
console.log("silent token acquisition fails.");
if (error instanceof msal.InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
console.log("acquiring token using redirect");
myMSALObj.acquireTokenRedirect(request);
}
else {
console.error(error);
}
});
}
So what happens upon going to this page is I get the two alerts saying "beginning handleResponse" and then "currentAccounts null/empty, going to signIn."
Then I'm redirected to MS sign-in page which I do with my work MS account. This succeeds.
I'm then redirected to the site I have listed in Azure Reply URLs, another page on our site that isn't secure and has no Azure login code.
The problem is I have no idea where to check that the user is signed in. If I try and check immediately after the signIn("loginRedirect") call in the handleResponse() function on the first page, the code never gets hit apparently. If I try and check on the page I'm redirected to, by instantiating the MSAL object and calling getAllAccounts(), this returns null.
It seems maybe on the page I'm redirected to I could call the ssoSilent() function (seems like this can check if user is authenicated?), but this requires a username/AccountId parameter. Well I don't frickin know this if a user hasn't (possibly) been authenticated yet! I don't really understand that.
So I don't know. It's probably something stupid I'm doing but I'm a pretty basic JavaScript person and am pretty much a total noob with authenication stuff. Any help would be epic.

Related

ErrorAccessDenied delete attachments with microsoft graph

I'm trying to remove attachments with Microsoft Graph. I have the following function:
getAccessToken(function(accessToken) {
if (accessToken) {
// Create a Graph client
var client = MicrosoftGraph.Client.init({
authProvider: done => {
// Just return the token
done(null, accessToken);
}
});
console.log(files);
files.forEach(function(file) {
client
.api(
"/me/messages/" +
file.msg +
"/attachments/" +
file.attachment
)
.delete((err, res) => {
if (err) {
console.log(err);
return;
} else {
console.log(res);
$("#attachment_" + file.list).remove();
}
});
});
} else {
var error = {
responseText: "Could not retrieve access token"
};
}
});
// OAUTH FUNCTIONS =============================
function buildAuthUrl() {
// Generate random values for state and nonce
sessionStorage.authState = guid();
sessionStorage.authNonce = guid();
var authParams = {
response_type: "id_token token",
client_id: appId,
redirect_uri: redirectUri,
scope: scopes,
state: sessionStorage.authState,
nonce: sessionStorage.authNonce,
response_mode: "fragment"
};
return authEndpoint + $.param(authParams);
}
function handleTokenResponse(hash) {
// If this was a silent request remove the iframe
$("#auth-iframe").remove();
// clear tokens
sessionStorage.removeItem("accessToken");
sessionStorage.removeItem("idToken");
var tokenresponse = parseHashParams(hash);
// Check that state is what we sent in sign in request
if (tokenresponse.state != sessionStorage.authState) {
sessionStorage.removeItem("authState");
sessionStorage.removeItem("authNonce");
// Report error
window.location.hash =
"#error=Invalid+state&error_description=The+state+in+the+authorization+response+did+not+match+the+expected+value.+Please+try+signing+in+again.";
return;
}
sessionStorage.authState = "";
sessionStorage.accessToken = tokenresponse.access_token;
// Get the number of seconds the token is valid for,
// Subract 5 minutes (300 sec) to account for differences in clock settings
// Convert to milliseconds
var expiresin =
(parseInt(tokenresponse.expires_in) - 300) * 1000;
var now = new Date();
var expireDate = new Date(now.getTime() + expiresin);
sessionStorage.tokenExpires = expireDate.getTime();
sessionStorage.idToken = tokenresponse.id_token;
validateIdToken(function(isValid) {
if (isValid) {
// Re-render token to handle refresh
renderTokens();
// Redirect to home page
window.location.hash = "#";
} else {
clearUserState();
// Report error
window.location.hash =
"#error=Invalid+ID+token&error_description=ID+token+failed+validation,+please+try+signing+in+again.";
}
});
}
function validateIdToken(callback) {
// Per Azure docs (and OpenID spec), we MUST validate
// the ID token before using it. However, full validation
// of the signature currently requires a server-side component
// to fetch the public signing keys from Azure. This sample will
// skip that part (technically violating the OpenID spec) and do
// minimal validation
if (
null == sessionStorage.idToken ||
sessionStorage.idToken.length <= 0
) {
callback(false);
}
// JWT is in three parts seperated by '.'
var tokenParts = sessionStorage.idToken.split(".");
if (tokenParts.length != 3) {
callback(false);
}
// Parse the token parts
var header = KJUR.jws.JWS.readSafeJSONString(
b64utoutf8(tokenParts[0])
);
var payload = KJUR.jws.JWS.readSafeJSONString(
b64utoutf8(tokenParts[1])
);
// Check the nonce
if (payload.nonce != sessionStorage.authNonce) {
sessionStorage.authNonce = "";
callback(false);
}
sessionStorage.authNonce = "";
// Check the audience
if (payload.aud != appId) {
callback(false);
}
// Check the issuer
// Should be https://login.microsoftonline.com/{tenantid}/v2.0
if (
payload.iss !==
"https://login.microsoftonline.com/" +
payload.tid +
"/v2.0"
) {
callback(false);
}
// Check the valid dates
var now = new Date();
// To allow for slight inconsistencies in system clocks, adjust by 5 minutes
var notBefore = new Date((payload.nbf - 300) * 1000);
var expires = new Date((payload.exp + 300) * 1000);
if (now < notBefore || now > expires) {
callback(false);
}
// Now that we've passed our checks, save the bits of data
// we need from the token.
sessionStorage.userDisplayName = payload.name;
sessionStorage.userSigninName =
payload.preferred_username;
// Per the docs at:
// https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-implicit/#send-the-sign-in-request
// Check if this is a consumer account so we can set domain_hint properly
sessionStorage.userDomainType =
payload.tid === "9188040d-6c67-4c5b-b112-36a304b66dad"
? "consumers"
: "organizations";
callback(true);
}
function makeSilentTokenRequest(callback) {
// Build up a hidden iframe
var iframe = $("<iframe/>");
iframe.attr("id", "auth-iframe");
iframe.attr("name", "auth-iframe");
iframe.appendTo("body");
iframe.hide();
iframe.load(function() {
callback(sessionStorage.accessToken);
});
iframe.attr(
"src",
buildAuthUrl() +
"&prompt=none&domain_hint=" +
sessionStorage.userDomainType +
"&login_hint=" +
sessionStorage.userSigninName
);
}
// Helper method to validate token and refresh
// if needed
function getAccessToken(callback) {
var now = new Date().getTime();
var isExpired =
now > parseInt(sessionStorage.tokenExpires);
// Do we have a token already?
if (sessionStorage.accessToken && !isExpired) {
// Just return what we have
if (callback) {
callback(sessionStorage.accessToken);
}
} else {
// Attempt to do a hidden iframe request
makeSilentTokenRequest(callback);
}
}
Access token
eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFDNXVuYTBFVUZnVElGOEVsYXh0V2pUUEF3aGltT2hjOXAxUkdiSnVjTDcyd0pjSFdTd1lpUXNFdDdqXzgxRVd6UXhvaWRaWnVXU2d5VS1HWXRqNFFNa3JjMUNpeFFTWElNRVpYQWhSUlJXZENBQSIsImFsZyI6IlJTMjU2IiwieDV0Ijoid1VMbVlmc3FkUXVXdFZfLWh4VnRESkpaTTRRIiwia2lkIjoid1VMbVlmc3FkUXVXdFZfLWh4VnRESkpaTTRRIn0.eyJhdWQiOiJodHRwczovL2dyYXBoLm1pY3Jvc29mdC5jb20iLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9iNmFjNDlmNy1iMTNlLTQyOWMtYmI4NS0wODQ4OTY5NTA2OTkvIiwiaWF0IjoxNTQ0MTkyODUxLCJuYmYiOjE1NDQxOTI4NTEsImV4cCI6MTU0NDE5Njc1MSwiYWNjdCI6MCwiYWNyIjoiMSIsImFpbyI6IjQyUmdZT0JiMFhGQjBNSHFySW5FbVU5aEc4T1p6MTllOHpIRld1S0sxbldqQzVrOFAvd0IiLCJhbXIiOlsicHdkIl0sImFwcF9kaXNwbGF5bmFtZSI6IkZvcm1wcmVzcyBjbGVhciBhdHRhY2htZW50cyIsImFwcGlkIjoiNTY0NzBlMjctZTAxNC00Zjg4LWEzY2QtZjQxODNlZjBhMjkxIiwiYXBwaWRhY3IiOiIwIiwiZmFtaWx5X25hbWUiOiJLYXJsc3NvbiIsImdpdmVuX25hbWUiOiJDaHJpc3RvcGhlciIsImlwYWRkciI6IjE1NS40LjE5NC4xMzQiLCJuYW1lIjoiQ2hyaXN0b3BoZXIgS2FybHNzb24iLCJvaWQiOiI5Y2JlMjBlYy1iMTk0LTRjZTYtOTU0Zi1hMTgzNTlmNGYzYTAiLCJwbGF0ZiI6IjMiLCJwdWlkIjoiMTAwMzAwMDA5QzI0MTI0NyIsInNjcCI6Ik1haWwuUmVhZCBvcGVuaWQgcHJvZmlsZSBVc2VyLlJlYWQgZW1haWwiLCJzaWduaW5fc3RhdGUiOlsia21zaSJdLCJzdWIiOiI5NVhONDhua08zbDJIeGYyTmdVTFo2YjA3WWpVV0lHOTRUQjA2bVNRUk9vIiwidGlkIjoiYjZhYzQ5ZjctYjEzZS00MjljLWJiODUtMDg0ODk2OTUwNjk5IiwidW5pcXVlX25hbWUiOiJjaHJpc3RvcGhlckBpdHBhcnRuZXJhYi5zZSIsInVwbiI6ImNocmlzdG9waGVyQGl0cGFydG5lcmFiLnNlIiwidXRpIjoiVmJGRW9EQ0RqMEMxVkFfSTNITWRBQSIsInZlciI6IjEuMCIsInhtc19zdCI6eyJzdWIiOiJka1FicDc3c2Y5ZmJlSnVYM1NqUml3aVhPa1FYOV9MZTF6MHp4X3Q4SU5BIn0sInhtc190Y2R0IjoxNDc4ODg0NzA1fQ.YQlKVdhXQcVwkcmbpY9Wx6ENro2DL7yH1rWwwkDZLD1inUrfbLRVb67lWKzgK9GnYP81d58Fp_2CZBw8C2E4X1eo02vog6_Qga9kVb8GF2-Ue0VP0KUv8EtRpEty_DBK7Re3iOkJR9yFSPQgf11Gf15l5O2mcEifrwny5nkRvab4_ssRt6hNf53V99uTFJ3_yKycGHPTobVbyQT5ZyDKxXRwoZVprFU70qrHGcBgo5emO8HbziYCUiQ9vGMpmtz61tE0U-c0E20FPC82i3zgLfMgmhNqmljZOpkOe85PFrxoep7fYkpZpWowCozugDW0E2A3SxBLZ_JHpci2R4irxg
files is a global variable that contains an array of objects, the data seems to be correct.
Here is a sample response I get when running the code:
{
statusCode: 403,
code: "ErrorAccessDenied",
message: "Access is denied. Check credentials and try again.",
requestId: "0739c0d9-38f2-45f7-a57d-c25dfbf92f75",
date: Fri Dec 07 2018 14:54:32 GMT+0100 (centraleuropeisk normaltid), …
}
API URL
me/messages/{id}/attachments/{id}
At https://apps.dev.microsoft.com/#/application/ I have given the application Mail.ReadWrite and Mail.ReadWrite.Shared access, though it only mentions read when signing up for the application which I find odd.
It looks like my app doesn't have the correct access. Is there something else I have to do that I have missed? Is my client query incorrect?
You need to check your scopes again. The Access Token you provided only includes the following scopes: Mail.Read openid profile User.Read email.
The code sample you provided references a scopes variable but it isn't clear what you've set this to:
var authParams = {
response_type: "id_token token",
client_id: appId,
redirect_uri: redirectUri,
scope: scopes,
state: sessionStorage.authState,
nonce: sessionStorage.authNonce,
response_mode: "fragment"
};
The value of this property should be something along the lines of openid profile email Mail.ReadWrite Mail.ReadWrite.Shared.

Using YouTube data api within chrome extension embedded iframe

I am trying to create a chrome extension that adds an iframe to an existing website and populates it with data from the YouTube data api however I am having trouble with restrictions imposed by the chrome extension content policy.
My current issue comes from user login, I need to use the gapi to get the users OAuth2 key however it appears that gapi isn't supported within sandboxed environments. Is it possible to use the YouTube data api without gapi? Or more directly is it possible have the code below work inside an iframe placed there by a chrome extension? This example comes from the Google docs.
<html><head><title>Google APIs - Sample JS Page</title></head>
<body>
<script>
/***** START BOILERPLATE CODE: Load client library, authorize user. *****/
// Global variables for GoogleAuth object, auth status.
var GoogleAuth;
/**
* Load the API's client and auth2 modules.
* Call the initClient function after the modules load.
*/
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
function initClient() {
// Initialize the gapi.client object, which app uses to make API requests.
// Get API key and client ID from API Console.
// 'scope' field specifies space-delimited list of access scopes
gapi.client.init({
'clientId': 'REPLACE_ME',
'discoveryDocs': ['https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest'],
'scope': 'https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtubepartner'
}).then(function () {
GoogleAuth = gapi.auth2.getAuthInstance();
// Listen for sign-in state changes.
GoogleAuth.isSignedIn.listen(updateSigninStatus);
// Handle initial sign-in state. (Determine if user is already signed in.)
setSigninStatus();
// Call handleAuthClick function when user clicks on "Authorize" button.
$('#execute-request-button').click(function() {
handleAuthClick(event);
});
});
}
function handleAuthClick(event) {
// Sign user in after click on auth button.
GoogleAuth.signIn();
}
function setSigninStatus() {
var user = GoogleAuth.currentUser.get();
isAuthorized = user.hasGrantedScopes('https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/youtubepartner');
// Toggle button text and displayed statement based on current auth status.
if (isAuthorized) {
defineRequest();
}
}
function updateSigninStatus(isSignedIn) {
setSigninStatus();
}
function createResource(properties) {
var resource = {};
var normalizedProps = properties;
for (var p in properties) {
var value = properties[p];
if (p && p.substr(-2, 2) == '[]') {
var adjustedName = p.replace('[]', '');
if (value) {
normalizedProps[adjustedName] = value.split(',');
}
delete normalizedProps[p];
}
}
for (var p in normalizedProps) {
// Leave properties that don't have values out of inserted resource.
if (normalizedProps.hasOwnProperty(p) && normalizedProps[p]) {
var propArray = p.split('.');
var ref = resource;
for (var pa = 0; pa < propArray.length; pa++) {
var key = propArray[pa];
if (pa == propArray.length - 1) {
ref[key] = normalizedProps[p];
} else {
ref = ref[key] = ref[key] || {};
}
}
};
}
return resource;
}
function removeEmptyParams(params) {
for (var p in params) {
if (!params[p] || params[p] == 'undefined') {
delete params[p];
}
}
return params;
}
function executeRequest(request) {
request.execute(function(response) {
console.log(response);
});
}
function buildApiRequest(requestMethod, path, params, properties) {
params = removeEmptyParams(params);
var request;
if (properties) {
var resource = createResource(properties);
request = gapi.client.request({
'body': resource,
'method': requestMethod,
'path': path,
'params': params
});
} else {
request = gapi.client.request({
'method': requestMethod,
'path': path,
'params': params
});
}
executeRequest(request);
}
/***** END BOILERPLATE CODE *****/
function defineRequest() {
// See full sample for buildApiRequest() code, which is not
// specific to a particular API or API method.
buildApiRequest('GET',
'/youtube/v3/search',
{'maxResults': '25',
'part': 'snippet',
'q': 'surfing',
'type': ''});
}
</script>
<button id="execute-request-button">Authorize</button>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script async defer src="https://apis.google.com/js/api.js"
onload="this.onload=function(){};handleClientLoad()"
onreadystatechange="if (this.readyState === 'complete') this.onload()">
</script>
</body></html>
I will refrain from posting any of my code as it's a total mess and will just confuse anybody who reads it. I also can't help but feel I am going in the wrong direction.
Any pointers as to how to achieve this would be greatly appreciated.

AngularJS & Socket.IO - Return value from Service (emit here) to Controller is undefined || promises, asynchronicity

I don't get my code to work properly.
I'm developing an app with AngularJS including a connection to a backend server via socket.io. I'm working on a login which is intended to send an user's data to the server. The server is intended to respond with "valid" and the user's data (name, dateOfBirth, ...) if the sent data is correct (email and password). Elements are:
BackendService (Factory which executes emit to server)
AppController (Controller which calls the login function of BackendService)
Node.js Server (computes if the sent data is valid so that the user can be logged in)
The intention is that the login function in the Factory returns a "login code" which tells the controller if the login is correct. Unfortunately the function returns "undefined". During my research, I found out that it might be because of asynchronicity and promises. However, I couldn't apply the given information to my problem as the majority was about $http. In addition - if the structure of my code is in need of improvement, let me know!
Here's my code:
Node.js Server
socket.on('logincust', function (p1, fn) {
connection.query("SELECT Salt FROM Customer WHERE Email = ?", [p1.Email], function (err, data, fields)
{
if (err) {
throw err;
}
if (data.length > 0) {
var hash = crypto.createHash('sha256').update(p1.Password + data[0].Salt).digest('base64');
connection.query("SELECT LName,FName,Email,Telephone,Address,DateOfBirth FROM Customer WHERE Password = ?", [hash], function (err, data2, fields) {
if (err) {
throw err;
}
if (data2.length > 0) {
fn('valid', data2[0]);
}
else {
fn('invalidpassword', 'nodata')
}
})
}
else {
fn('invalidemail','nodata')
}
})
})
BackendService (Factory)
"use strict";
mobileClientApp.factory('BackendService', function() {
var mySocket = io.connect('http://thisLinkIsPrivate:8888/ns');
return {
login: function (pUserData) {
if (mySocket.connected) {
mySocket.emit('logincust', pUserData, function (resp, data) {
if (resp == "valid") {
var parsedData = JSON.stringify(data);
console.log(parsedData);
user.lName = parsedData.LName; // Fill userData
user.fName = parsedData.FName;
user.email = parsedData.Email;
user.phoneCallcenter = parsedData.Telephone;
console.info("Login successful.");
return 0;
}
else {
if (resp == "invalidpassword") {
return 1;
}
else if (resp == "invalidemail") {
return 2;
}
}
});
}
else { // Socket is not connected
console.warn("Socket not connected.);
user.fName = "Peter";
user.lName = "Offline";
return -1;
}
};
Angular Controller
$scope.doLogin = function() {
var user = {'Email': this.loginData.username, 'Password': this.loginData.password};
var isLoggedIn = BackendService.login(user); // 0 - logged in, 1 - invalid password, 2 - invalid email, -1 - socket not connected
console.log("BackendService.login(user): " + BackendService.login(user)); // is undefined!
console.log("isLoggedIn: " + isLoggedIn); // undefined!
if (isLoggedIn == 0 || isLoggedIn == -1) {
$location.path('/app/landing');
}
else {
$scope.errorMessage = "Invalid login data!";
}
};
Yes, the problem seems to be asynchrony. If you want to have access to results from login method you should pass a callback to it. Since after you called it, the next execution will be your console log and that will happen before your SQL query returns results.

Exception in async function: Only on server, not on localhost

I am trying to get a route working that will function as a "Thank You" page for people who buy our products on an external store. On localhost everything works fine but on our staging server I get the following exception:
Exception in callback of async function: action#http://taskwunderstaging-45398.onmodulus.net/12289f8cf999b67e6c6c6dcad1a5a5eded53f4e2.js:517:468
Does anyone have an idea what might be causing this?
The code in question is as follows:
The Iron Router Endpoint
Router.route('/signup-partner', {
name: 'signupPartner',
where: 'client',
waitOn: function(){
return Meteor.subscribe("packages");
},
action: function() {
Meteor.logout(function() {});
var query = this.params.query;
//#TODO verify the query with the sha key!
var userInfo = {
email:query.email,
firstname:query.firstname,
lastname:query.lastname,
};
var companyInfo = {
companyName:query.company,
street:query.street,
city:query.city,
zipcode:query.zipcode,
country:query.country,
taxId:query.taxid
};
var orderInfo = {
product:query.product,
order:query.order,
};
// get the package from the database
orderInfo.package = Packages.findOne({digistoreId:orderInfo.product}).name;
orderInfo.tw_id = Packages.findOne({digistoreId:orderInfo.product})._id;
var data = {
userInfo:userInfo,
companyInfo:companyInfo,
orderInfo:orderInfo,
};
var that = this;
// check if the user account already exists and if so add the package and login the user
Meteor.call("partnerUserExists", data.userInfo.email,{orderId:data.orderInfo.order,tw_id:data.orderInfo.tw_id}, function(error, result){
if(result === "not-found"){
that.render('signup_partner',{
data: function(){
return data;
}
});
}
else {
Session.set('boughtPackage',result);
that.redirect('login');
}
});
}
});
the method that this route calls is as follows:
partnerUserExists: function(email,orderIds){
var user = Meteor.users.findOne({"emails.address":email}) || false;
console.log(user);
if(!user){
return "not-found";
}
if(_.indexOf(user.data.digistoreOrders,orderIds.orderId) > -1){
return orderIds.tw_id;
}
(function(callback){
// add the paidTask array if it doesnt exist
if (!user.data.paidTasks){
Meteor.users.update({_id:user._id},{$set:{"data.paidTasks":[]}});
}
// add the digistore array if it doesnt exist
if (!user.data.digistoreOrders){
Meteor.users.update({_id:user._id},{$set:{"data.digistoreOrders":[]}});
}
callback();
})(function(){
Meteor.users.update({_id:user._id},{$push:{"data.digistoreOrders":orderIds.orderId}});
Meteor.users.update({_id:user._id},{$push:{"data.paidTasks":orderIds.tw_id}});
return orderIds.tw_id;
});
}
check for error in your meteor.call. It should tell you if there is an error and why. If not, then try putting a console.log before each return. Overall, I see a lot of user.xx fields being accessed without checking whether that field is set. It could be one of those.

How to build Twitter OAuth authentication?

This is my JavaScript code for following operation: https://api.twitter.com/1.1/users/show.json?screen_name=barackobama
Does not work anymore since the Twitter API 1.1: {"errors":[{"message":"Bad Authentication data","code":215}]}
I know you need the OAuth access Token now, I even created one in Twitter, but I don't know how to add it in this Script.
Thanks for any Help!
function TwitterFollowers(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.followers_count;
}
function TwitterFollowings(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.friends_count;
}
function TwitterListed(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.listed_count;
}
function TwitterId(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.id;
}
function TwitterFullname(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.name;
}
function TwitterCreatedDate(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.created_at;
}
function TwitterVerified(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.verified;
}
function TwitterTimezone(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.time_zone;
}
function TwitterLocation(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.location;
}
function TwitterHomepage(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.url;
}
function TwitterDescription(aUsername)
{
user = _twitterFetchUserData(aUsername);
return user.description;
}
// internal function invisible to Google SpreadSheets
var _twitterFetchUserData = function(aUsername)
{
if (aUsername === undefined || aUsername === null)
throw "No parameter specified. Write any Twitter USERNAME as parameter."
if (aUsername == "")
throw "USERNAME cannot be empty string. Write any Twitter USERNAME as parameter.";
// See https://dev.twitter.com/docs/api/1/get/users/show for API documentation
var url = "https://api.twitter.com/1.1/users/show.json?screen_name=" + encodeURIComponent(aUsername);
try
{
var response = UrlFetchApp.fetch(url);
}
catch (e)
{
throw "Please check if Twitter Username \"" + aUsername + "\" exists. " + e;
}
if (response.getResponseCode() != 200)
throw "Unexpected response code from Twitter.";
var responseText = response.getContentText();
if (responseText == null || responseText == "")
throw "Empty response from Twitter.";
var user = Utilities.jsonParse(responseText);
if (user == null)
throw "Problem with response from Twitter. Invalid JSON.";
return user;
};
According to the new version Twitter API v1.1 all requests to Twitter must be signed by using OAuth 1.0A. You may want to visit here to see how it works. I would suggest to use Twitter libraries to authenticate and sign your requests like codebird-js(have a look at the examples below or at the website given). It's really easy to use. To authenticate is as simple as below:
var cb = new Codebird;
cb.setConsumerKey('YOURKEY', 'YOURSECRET');
cb.setToken('YOURTOKEN', 'YOURTOKENSECRET');
and then make calls e.g. tweet:
cb.__call(
'statuses_update',
{'status': 'Whohoo, I just tweeted!'},
function (reply) {
// ...
}
);
Hope it helps.

Categories