I have created an outlook add-in with ReactJS and followed this guide to get a token to be able to use the Outlook v2.0 REST APIs: https://learn.microsoft.com/en-us/office/dev/add-ins/outlook/use-rest-api
Now I would like to start using the Outlook Beta REST APIs and I figured I could use the same token to make the API calls, however I get the following error which suggests I cannot use this token:
{"error":{"code":"UnableToReadToken","message":"OAuth token submitted with the request can not be parsed.","innerError":{"requestId":"b96fc800-82d4-4b6d-8aa0-0b9ff6a36873","date":"2020-02-21T09:27:27"}}}
Is there anyway to call this API by using the token generated by Office.context.mailbox.getCallbackTokenAsync? I am aware that I can probably get an oauth2 token via Azure AD, however within the Azure AD Portal I do not have the proper admin access to follow this process so I am looking for a solution which does not rely on that.
Here is a code snippet of my functions to get the token and call the API:
getToken() {
return new Promise(async function (resolve, reject) {
try {
Office.context.mailbox.getCallbackTokenAsync({ isRest: true }, function (result) {
if (result.status === "succeeded") {
let accessToken = result.value;
console.log(result.value);
resolve(accessToken);
} else {
console.log(result.status);
reject(result.status);
}
});
} catch (error) {
console.error(error);
reject(error);
}
})
}
getRules(token) {
return new Promise(async function (resolve, reject) {
try {
const url = 'https://outlook.office.com/api/beta/me/mailfolders/inbox/messagerules';
const header = new Headers({ 'Authorization': `Bearer ${token}` });
const options = {
headers: header
};
let response = await fetch(url, options);
let jsonResponse = await response.json();
console.log(jsonResponse);
resolve(jsonResponse);
} catch (error) {
console.error(error);
reject(error);
}
});
}
You mention not having the proper admin access to use the AD v2 authentication endpoint.
There are currently two approaches to handle app registration and user authorization. Did you confirm, if by chance one of these methods might still work...
Use Azure AD v2 authentication endpoint:
https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/beta/use-outlook-rest-api-beta#RegAuthConverged
Use Azure Active Directory and OAuth:
https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/beta/use-outlook-rest-api-beta#RegAuthAzure
...
Some additional information (which you might already be aware of):
The v2 authentication endpoint has been promoted from preview to Generally Available (GA) status for Outlook and Outlook.com developers.
If you have an in-production app that uses Windows Live API to access Outlook.com mailbox data, you must rewrite the app to use the the v2 authentication endpoint and the Outlook REST API. Because Windows Live API is being deprecated for Outlook.com, and Outlook.com users get their mailboxes enabled for the Outlook REST API, these users will get HTTP 404 errors when attempting to run such Windows Live API apps.
Read more here: https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/beta/use-outlook-rest-api-beta
Related
So, I am new at node and decided to create a user authentication/authorization system. Btw, I am using ejs as view engine and not any front-end framework. Everything works when I use postman to check API. Users are registered, tokens are generated while logging in and most importantly authorization also works when I manually put generated token in header (using postman). But how could I extract those tokens and put them in headers without postman? I saw some implementations using axios, fetch etc. But all of them were using front-end frameworks to do that.
Logging in users
Accessing protected route without token
Accessing protected route with token
UPDATE:
So the problem was in passport.authenticate() method and I added my personal verification method to extract cookies specifically.
const verifyJWT = (req: Request, res: Response, next: NextFunction) => {
const signedToken = req.cookies.jwt;
if (signedToken) {
jwt.verify(
signedToken,
config.PRIV_KEY,
{ algorithms: ["RS256"] },
(err: any, decodedToken: any) => {
if (err) {
// tslint:disable-next-line:no-console
console.log(err);
} else {
// tslint:disable-next-line:no-console
console.log(decodedToken);
next();
}
}
);
} else {
res.send("Unathorized");
}
};
You don't have to use frameworks but you do need to use javascript. Whatever you do with postman you can do using fetch or axios, you can save the tokens in your cookie.
I have created a web scene on my arcgis online portal and hosted it there also. Now I want to load the webscene on map through arcgis javascript api v4.1.6 and I want to pass the credential(like a token which I can get from argis js api with the right client id and client secret) through code.
Here is my code for loading the web scene
let scene = new WebScene({
portalItem: { // autocasts as new PortalItem()
id: "0614ea1f9dd043e9ba157b9c20d3c538" // ID of the WebScene on the on-premise portal
}
});`
let myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var formdata = new FormData();
formdata.append("client_id", "");
formdata.append("client_secret", "");
formdata.append("grant_type", "client_credentials");
formdata.append("expiration", "20160");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: formdata,
redirect: 'follow'
};
let token = await fetch("https://www.arcgis.com/sharing/rest/oauth2/token", requestOptions)
When I want to check the map in my website, it always prompt a popup window and ask for user name and password. So I am curios is it possible to feed the token somewhere in the code when I load the web scene? So it won't ask username and password from user.
Can you please provide me some sample code in ArcGIS API JavaScript v4.1.6?
Thanks!
It is possible to bypass the login prompt with the Esri Resource Proxy. However, the README does say that "it is not permitted to embed credentials in a resource proxy for the purpose of bypassing Named User authentication (i.e. the principle that each end-user must have their own unique login)".
Here is another possible workflow:
Pass the token generated at https://www.arcgis.com/sharing/rest/oauth2/token in a registerToken() to access the non-public items. With that, every AJAX request made by the application forwards this token when accessing web maps and other items stored in ArcGIS Online, or resources on your server.
var url = "https://www.arcgis.com/sharing/rest/oauth2/token";
var token = "";
esriRequest(url, {
query: {
client_id: "<CLIENT_ID>",
client_secret: "<CLIENT SECRET>",
grant_type: "client_credentials"
},
method: "post"
})
.then((response) => {
token = response.data.access_token;
esriId.registerToken({
server: "https://www.arcgis.com/sharing/rest",
token: token
})
})
.catch((err) => {
if (err.name === 'AbortError') {
console.log('Request aborted');
} else {
console.error('Error encountered', err);
}
});
A few things to note about this workflow:
The non-public items have to be owned by the same user who generated the client id and secret.
The layers on the web map / scene have to be either public or owned by the user who generated the client id and secret. That said, if you do need to include a non-public layer created by another user, you can create an item referencing that layer using the following workflow, and then add this new item to the web map / scene -
Add items from the web: https://doc.arcgis.com/en/arcgis-online/manage-data/add-items.htm#ESRI_SECTION1_1A21D51E1AFC41EA9974209BD94E50C0
Related documentation:
IdentityManager.registerToken: https://developers.arcgis.com/javascript/latest/api-reference/esri-identity-IdentityManager.html#registerToken
request.esriRequest: https://developers.arcgis.com/javascript/latest/api-reference/esri-request.html#esriRequest
If you want do it public, that is what I am understanding, you can set proxy that handle the security of the services. ESRI have open resources for similar tasks, take a look at this,
ESRI Git - Resources - Proxy
I am using AWS API gateway for API's and cognito UserPool's for security. After Authenticating the user we will get tokens and I am using that token to authorise my API.
Now, I am trying to enable signout to cognito authorised users using javascript. Used the below code.
if (cognitoUser != null) {
cognitoUser.globalSignOut({
onFailure: e => console.log(e),
onSuccess: r =>
console.log('Logout success: ' + r)
})}
I am getting response as success but still I am able to access my API with the previous tokens.Please suggest me how to inactivate all the tokens issued to that cognito user.
The id token, which API Gateway uses to authenticate API calls, stays valid for a while.
I would test for the access token. It should expire right after you call global sign out.
The key word is should above. Please see this issue. It’s an on-going struggle to get AWS to implement an immediate revocation. Here’s a relevant quote:
I worked with AWS Cognito team to get this taken care and got released as a fix through CLI as following.
aws cognito-identity update-identity-pool --identity-pool-id --identity-pool-name --allow-unauthenticated-identities --cognito-identity-providers ProviderName=,ClientId=,ServerSideTokenCheck=<true|false>
By setting the ServerSideTokenCheck to true on a Cognito Identity
Pool, that Identity Pool will check with Cognito User Pools to make
sure that the user has not been globally signed out or deleted before
the Identity Pool provides an OIDC token or AWS credentials for the
user. Now we are running into another issue of this Token being cached
in API Gateway for 10mins which would let that OID token still be
active for 10mins even though the User has globally signed out.
Here's what I mean by test for the accessToken (I have had success with method #2):
1.) you could develop a custom authorizer for API Gateway;
2.) you could perform a check at the start of your lambda functions or on your servers, using:
const AWS = require('aws-sdk');
const awsConfig = require('./awsConfig');
const cognito = new AWS.CognitoIdentityServiceProvider(awsConfig);
// accessToken provided from API Gateway
new Promise((resolve, reject) => {
cognito.getUser({ accessToken }, (errorCallback, response) => {
if (errorCallback) {
reject(errorCallback);
} else {
resolve(response);
}
});
});
The errorCallback and response do not matter. If you get an error, the token is invalid. If you don’t, it’s valid.
I have developed an Outlook add-in project for Outlook desktop app with identity server authentication, but it fails to access the identity server through code for authentication.
Below is the code to connect with identity server
return await this.http.post('https://localhost:5050/connect/token', body, { headers : headers})
.pipe(map(response => {
debugger;
let result = response.json();
if (result && result['access_token']) {
localStorage.setItem('token', result['access_token']);
return true;
}
else return false;
}));
Following is the full code of the function
async login(credentials) {
let body = `grant_type=password&username=${credentials['username']}&password=${credentials['password']}&scope=api1`;
let headers = new Headers({'Authorization' : 'Basic cm8uY2xpZW50OnNlY3JldDI='});
headers.append('Accept', 'application/json');
headers.append('Content-Type', 'application/x-www-form-urlencoded');
let options = new RequestOptions({ headers : headers});
return await this.http.post('https://localhost:5050/connect/token', body, { headers : headers})
.pipe(map(response => {
let result = response.json();
if (result && result['access_token']) {
localStorage.setItem('token', result['access_token']);
return true;
}
else return false;
}));
}
Note:
App is designed for Outlook Desktop App and not for Outlook365 online.
Identity server runs in HTTPS.
When connects with the add-in through Outlook, login page loads successfully but when entering user name and password to log, the above code fails to connect with identity server.
Identity server is implemented in a separate project, runs in HTTPS and it works fine when running the web application without implementing it as an Outlook add-in project.
The same Outlook adding project works fine if there is no identity server authentication implemented.
Can access any APIs runs in Identity server which runs in HTTPS,
ex - https://localhost:5050/api/User
but when calling 'https://localhost:5050/connect/token' to get the taken, no response and Token cannot be received
Could you please any idea to solve the above problem to develop Outlook add-in with identity server authentication
Currently having issues accessing a web api controller that has the an autherize attribute and is registered to Azure AD. Currently I am getting a Token from ADAL.JS successfully and now am trying to make a ajax call to a test Webapi which will perform a back end service for my Office-Addin app. I have tried to scenarios which I will describe.
First Scenario:
I created to separate web application entries Azure management portal, one for my office Add in app so I can get a token, and the other one for my web api to I can lock it down. I then gave permission for my office-add application to my webapi in order for my office-Add in to talk with my web api. I get a 401 status unauthorized.
Second Senario:
Since I was getting unauthorized I proceeded to make a new application in my Azure Management portal and getting a token with the ADAL.js but when I make the same call to the webapi which is sharing the same client number as my office-Addin app I still get a 01 status unauthorized.
Not sure what I am doing wrong, seem like I have tried both possible ways but none are working for me. This is my java-script
window.config = {
tenant: variables.azureAD,
clientId: variables.clientId,
postLogoutRedirectUri: window.location.origin,
endpoints: {
sharePointUri: "https://" + tenant + ".sharepoint.com",
ContentCenterApi: "https://localhost:44334"
},
cacheLocation: "localStorage"
};
var authContext = new AuthenticationContext(config);
var isCallback = authContext.isCallback(window.location.hash);
authContext.handleWindowCallback();
if (isCallback && !authContext.getLoginError()) {
window.location = authContext._getItem(authContext.CONSTANTS.STORAGE.LOGIN_REQUEST);
}
function GetSharepointList(e) {
var authContext = new AuthenticationContext(config);
var user = authContext.getCachedUser();
if (!user) {
authContext.login();
}
else {
authContext.acquireToken(config.endpoints.sharePointUri, function (error, token) {
if (error || !token) {
console.log("ADAL error occurred: " + error);
return;
}
else {
var me = this;
//var SiteUrl = config.endpoints.sharePointUri + "/sites/Apps_Dev/ER_TestSite/";
$.ajax({
url: 'https://localhost:44334/api/Values/Get',
dataType: "json",
headers: {
"Authorization": "Bearer " + token,
"accept": "application/json;odata=verbose",
},
success: function (data) {
handleData(data);
}
}).done(function (response) {
console.log("Successfully fetched list from SharePoint.");
// var items = response.d.results;
//$("#contentPreview").append(items[0]);
}).fail(function (error) {
console.log("Fetching list from SharePoint failed.");
});
}
});
};
}
I'm a little confusing that the API you call is
"https://localhost:44334/api/Values/Get", but the resource id to acquire token is "config.endpoints.sharePointUri". It should be the App URI you registered on the Azure AD.
About how to secure the Web API by using Azure AD. The article below may give you some help.
Protect a Web API using Bearer tokens from Azure AD.
Single Page Application demo (adal.js): active-directory-angularjs-singlepageapp
This sample demonstrates the use of ADAL for JavaScript for securing an AngularJS based single page app, implemented with an ASP.NET Web API backend.