Reputation: 1400
I have an Angular application that lets users upload files. I am planning to store these files in the OneDrive
using OneDrive API
(the OneDrive account I have set up for the application).
I am aware that we have to use OAuth2.0
to get the access token from the webserver and use that token as a bearer token to use the API to manage my files in OneDrive.
How can I get this access token in my Angular app?
I need to get the access token without redirecting to the login page(Without interaction from the user). but in the background.
I tried the following URL to get the access token in POSTMAN.
I have tried,
Function for Implicit flow in Angular (I have hardcoded the URL and values to test)
getToken() {
var msFormData = new FormData();
msFormData.append('grant_type', 'client_credentials');
msFormData.append('client_id', 'client_id');
msFormData.append('client_secret', 'client_secret');
msFormData.append('resource', 'https://graph.microsoft.com');
return this.http.post("https://login.microsoftonline.com/{id}/oauth2/token", msFormData);
}
All three are working and able to get the token. When I tried implicit flow in Angular I am getting a CORS error. (I can't use implicit anyway because the client secret will be exposed).
When tried with the
Resource_owner
flow I got theSPO license
error message. Where if I use theuser flow
and retrieve the access token from the redirect URL. I am able to use the Graph API with that access token to get the drive items. So I know I don't need an SPO license for this.(Maybe)
If that is not the best way I can create a service in my backend to get the access token/refresh token and serve it to the Angular app using an API, so the users can upload the files from the browser. But the access token got from the above flow gives me an SPO error.
UPDATE: I found out that to access the one drive we need a delegated access token. How is that different from client_credenttial flow? and how to get them?
Upvotes: 4
Views: 3535
Reputation: 663
In case anyone else has a similar issue, a few weeks ago, when I was trying to do the same thing as OP in my angular application. As the company that I work on uses a personal Microsoft account, it was not possible to use non-interactive flows[1][2] to access the company one drive. So as the company employees already have the company one drive as a shared folder, we were finding a new login solution, and we only can use interactive ways to get access to the Graph API. We embrace MSAL and change our login method to use the employee's personal Microsoft account.
So to log in, we use @azure/msal-angular
that also provides an HTTP interceptor and the popup strategy. Thus the employee fills their Microsoft credentials in the popup using an Oauth2 Token Flow
. Once logged in, the MSAL_INTECEPTOR refreshes our token and puts the authorization code when making a request to Graph API. The changes in our code are described in these 2 commits[3][4]. With this part ready, now we can make a request to Onedrive API to upload files to the company shared folder that all employees have. I shared some tips about upload to shared folders here: Microsoft Graph: Uploading files to shared with me folder on OneDrive?.
I really liked the results with MSAL that I have, and the company employees also loved the direct Onedrive integration. Because they just need to login into our application one time and it's all set.
I shared a step by step Postman Oauth2 Code Flow
to exercise the Onedrive file upload to personal accounts here: Tenant does not have a SPO license
Upvotes: 1
Reputation: 9519
The delegated token is generally called a user token
, and the token you get through the client credential flow is an application token
. The only difference between them is whether there is a user logged in.
If you want to obtain a delegated token, must log in user. Whether it is an interactive login or a non-interactive login, the user must be logged in!
In addition, you need change the /tenant id
endpoint to the /common
endpoint to avoid tenant-level login.
Upvotes: 1
Reputation: 91
To upload a file on one drive in angular involves three steps,
First step is when user Authenticate with Azure AD and this will happen interactively. We can use MSAL library to get the id token. Link mentioned below has all the documentation for that,
https://learn.microsoft.com/en-us/graph/auth-v2-user#1-register-your-app
Before second step in in Azure AD App we need map configure protected resource map and add graph API url over there with scope as "Files.ReadWrite.All". And then try to get the token for graph api token in the background with 3rd step mentioned in the link mentioned above. Graph API token has been generated using client secrete.
Once we receive the Graph API token then we can send the file. For that you can use the links mentioned below,
how to specify the stream to upload a file to onedrive using ms graph
Upvotes: 1
Reputation: 45101
To get a valid graph token without user interaction you could use the following request. But be aware, this means in your client code somewhere the client secret is stored. And with this, the user could be able to request a valid application token and could access anything that is allowed in application scope.
So, like the name already states, the client secret should be kept secret and not be used in code on the client site. Better would be to have an own REST api, that sends the below request and returns the token to your angular application. How you secure this api is up to you.
var settings = {
"async": true,
"crossDomain": true,
"url": `https://login.microsoftonline.com/${tenantId}/oauth2/token`,
"method": "POST",
"headers": {
"content-type": "application/x-www-form-urlencoded",
"cache-control": "no-cache"
},
"data": {
"client_id": `${clientId}`,
"client_secret": ${clientSecret}``,
"resource": "https://graph.microsoft.com/",
"username": `${username}`,
"password": `${password}`,
"grant_type": "password",
"scope": "openid"
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});
Just though, you would be able to transform the above request to your concrete language. But as a special service I rewrote the above code to your given code (without testing):
getToken() {
var msFormData = new FormData();
msFormData.append('clientId', `${clientId}`);
msFormData.append('client_secret', `${clientSecret}`);
msFormData.append('resource', 'https://graph.microsoft.com');
msFormData.append('username', `${username}`);
msFormData.append('password', `${password}`);
msFormData.append('grant_type', 'password');
msFormData.append('scope', 'openid');
return this.http.post(`https://login.microsoftonline.com/${tenantId}/oauth2/token`, msFormData);
}
var settings = {
"async": true,
"crossDomain": true,
"url": `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
"method": "POST",
"headers": {
"content-type": "application/x-www-form-urlencoded",
"cache-control": "no-cache",
},
"data": {
"grant_type": "client_credentials",
"scope": "https://graph.microsoft.com/.default",
"client_id": `${clientId}`,
"client_secret": `${clientSecret}`
}
}
$.ajax(settings).done(function (response) {
console.log(response);
});
Instead of using backtick strings, that only contain the variable, you could also use the variable itself directly. But I choosed this style to better highlight, that these parameters have to be provided by yourself.
Upvotes: 3