How to set up access to Azure blob storage from React app (deployed in Azure web app) with credentials from browser? - reactjs

I got stuck on trying to access Azure blob storage from React app (thus from browser) with credentials.
And firstly I want to admit, that I am newbie in Azure, so maybe I misunderstood some basic concepts...
My current situation and goal:
I am developing React app (lets say MyReactApp). This app uses some files from Azure blob storage (lets say MyBlobStorage) -> it reads, creates and deletes blobs.
I started to develop it on my local and for this dev purpose I was connecting to MyBlobStorage with SAS - this worked perfectly.
MyReactApp is browser only, so it does not have any backend.
After finishing local development, I deployed it as Azure web app with SAS. What have I done:
created App service (lets say MyAppService)
register app in Azure Active Directory and use it as Identity Provider in MyAppService
After this the app works from Azure url perfectly too.
But my app on Azure should fulfill 2 conditions:
User has to log in with AAD account before access to MyReactApp
App itself must get rid of SAS (because it is not secure as it can be obtained from browser) and use some Azure credentials to connect to Azure blob storage
First condition - user log in:
I enabled "easy" logging in MyAppService -> Authentication and chose users, who can have access.
in Authentication section of app in AAD I set up Web type Redirect Uri as /.auth/login/aad/callback
Still everything works great - the user, who is assigned to the app, can log in and work with the app - so far so good, but now the problem comes
Second condition - I wanted to get rid of the SAS to access MyBlobStorage and use DefaultAzureCredentials:
I turned on managed identity for MyAppService and add it as Storage Blob Data Contributor for MyBlobStorage
I obtained user_impersonation and User.Read Api permissions for my app
I removed SAS and tried to add DefaultAzureCredentials to my code -> but it seems, that they can't be used in browser and only option is InteractiveBrowserCredentails
so I tried to use InteractiveBrowserCredentails like this:
this.interactiveBrowserCredential = new InteractiveBrowserCredential({
tenantId: "<appTenant>", // got from app registration on AAD
clientId: "<appClient>", // got from app registration on AAD
redirectUri: <MyAppServiceURi>/.auth/login/aad/callback // the same as in Azure AAD app,
});
this.blobSC = new BlobServiceClient(Constants.STORAGE_PATH, this.interactiveBrowserCredential);
My current result:
This shows login popup after getting to the page and after another signing in it results in error:
AADSTS9002326: Cross-origin token redemption is permitted only for the
'Single-Page Application' client-type.
I googled it of course and according to some answers I tried to change the Web type of redirect URI to SPA.
I tried it, but some other error showed up:
AADSTS9002325: Proof Key for Code Exchange is required for
cross-origin authorization code redemption.
Surprisingly this should be solved by changing from SPA to Web type...:) So I am trapped...
My expected result
In ideal world, I want to connect MyReactApp to MyBlobStorage without popup and with some secret credentials, just to say something like this.blobSC = new BlobServiceClient(Constants.STORAGE_PATH, credentials);
and be able to work with blobs.
Is it possible to access blob storage from browser without errors of course and ideally without popup, which needs another log in for user?
My complementary questions
Can I use somehow the logging info from the user (from his "easy" AAD logging)? I can get his info with GET call to /.auth/me, maybe it can be utilized, so I can use his info to access the blobs?
The solution should be working on localhost too (I tried to add http://localhost:3000/ to redirect uri, but without success), can it be done?
Thank you all, who read the whole story!

Related

Error "invalid_client" when trying to get a token from Microsoft API

I'm trying to develop a drive solution (Onedrive) in a windev program.
I created an application in Microsoft Azure and created a secret key.
When doing the first request https://login.live.com/oauth20_authorize.srf?client_id={client_id}&scope={scope} &response_type=code&redirect_uri={redirect_uri} I'm redirected on the connection page.
Once I'm connected I get a code back as https://login.live.com/oauth20_authorize.srf?code={code}.
But when I ask for a token posting this request : POST https://login.live.com/oauth20_token.srf Content-Type: application/x-www-form-urlencoded client_id={client_id}&redirect_uri={redirect_uri}&client_secret={client_secret} &code={code}&grant_type=authorization_code
I get this back
{ "error":"invalid_client", "error_description":"The client does not exist or is not enabled for consumers. If you are the application developer, configure a new application through the App Registrations in the Azure Portal at https:\/\/go.microsoft.com\/fwlink\/?linkid=2083908.", "correlation_id":"471e800c-69b4-43c6-a03f-a1f7e9512e6b" }
Thank you for your help.
This error means you are using a Microsoft Account to login your client app, but it is not enabled for that.
To change the setting for an existing AD App, navigate to the Manifest blade of it in the portal, find the signInAudience attribute, set it with AzureADandPersonalMicrosoftAccount or PersonalMicrosoftAccount.

How to Authenticate WPF Application with AAD B2C to gain access to Azure App Service

I'm quite stuck at the moment trying to implement authentication into a project I'm working on. The end goal of this project is to have two WPF apps and on web based app hosted on Azure. One WPF app is for an administrator, the other for staff, and lastly the web app for customers. Each application will be connected to one Azure App Service for a shared database and needs to have authentication so separate all the users. For authentication I am planning on using Azure Active Directory B2C.
I've been researching and trying to implement this for several days now on one of the WPF apps but as I stated before I'm quite stuck. From what I understand, the only way to do B2C authentication for WPF is through client managed authentication. Following the code shown on the Azure tutorial sites, other SO posts, and the Azure Git Repos, I have come up with the following code:
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
authResult = await App.PublicClientApp.AcquireTokenAsync(App.ApiScopes,
GetUserByPolicy(accounts, App.PolicySignUpSignIn), UIBehavior.SelectAccount,
string.Empty, null, App.Authority);
Newtonsoft.Json.Linq.JObject payload = new Newtonsoft.Json.Linq.JObject();
payload["access_token"] = authResult.AccessToken;
MobileServiceClient msclient = new MobileServiceClient(App.AzureAppService);
MobileServiceUser user = await msclient.LoginAsync(
MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
Everything starts off great and I'm able to get my Sign-In policy to display. After logging in, I am given an IdToken and an AccessToken. After creating a JObject and adding the access token to it, I attempt to use it to login with my MobileServiceClient. But that's where I am having issues. No matter what I do, no matter what I try, I only get an exception with a 401 Error telling me I'm unauthorized. And this is the point I've been stuck at for the past few days.
Obviously I'm not doing anything special here and I imagine many people have done this before me but I just can't seem to get past this point and was hoping someone may be able to offer me some guidance. Am I way off track? Is there a better way that I could be doing this? Any suggestions or advice would be greatly appreciated as I am very new to Azure.
Thanks all!
Update:
Here's how I have my Azure Settings:
On the app service side
Client Id: "{Client Id of the AAD B2C App}"
Issuer URL: "login.microsoft.com{TennatName}.onmicrosoft.com/v2.0/.well-known/openid-configuration"
Allowed Token Audiences: "https://{App Service Name}.azurewebsites.net" (App Service URL)
On B2C side:
Web and native client enabled
Web Reply URL: "https://{AppServiceName}.azurewebsites.net/.auth/login/add/callback"
Native App: I did not know what custom redirect URL to have so I have both
"{TennatName}.onmicrosoft.com://auth/" and
"{AppServiceName}.azurewebsites.net/.auth/login/add/callback"
Update 2:
My authority is login.microsoftonline.com/tfp{tenant}/{policy}/oauth2/v2.0/authorize
And my ApiScopes = { "https://{Tenant}/thisisatest/user_impersonation" };
If the authority for the client is set to https://{your-tenant-name}.b2clogin.com/tfp/{your-tenant-name}.onmicrosoft.com/{your-policy-name}/, then the issuer URL in the app service must refer to the metadata for this authority; i.e. https://{your-tenant-name}.b2clogin.com/tfp/{your-tenant-name}.onmicrosoft.com/{your-policy-name}/v2.0/.well-known/openid-configuration.

Microsoft Graph appRoleAssignment direct queries not supported?

I've been using these github documentation pages: Page1, Page2, I've implemented adal on the app that I'm using and I'm just logging the token on the console so that I can just copy-paste the token into postman and try requests faster.
I've been trying a get request as mentioned on the documentation to.
https://graph.microsoft.com/beta/appRoleAssignments/{Object Id of AD App} or { App ID of AD App }
But also attempted various other routes based on the documention. I'm getting 'resource not found' for all of the other ones and 'direct queries to this resource type are not supported' for the one above.
Brosed a few related stackoverflow threads and went into github issues to see anything related to approlesassignment resource type but no luck.
EDIT: Token is obtained in Angular app using package adal-angular4 (it is however an angular6 application and the package has been updated to work with Angular 6)
After a lot of struggling, I managed to get the necessary info via Azure Enterprise App API, not th endpoints mentioned above.
This is how the GET request looks like:
https://graph.microsoft.com/beta/servicePrincipals/{ID}/appRoleAssignments/
ID in the context above is "Object ID" of the Azure AD App under 'ENTERPRISE APPS', taking the one from under 'enterprise apps' is critical.
You'll find it in:
In Azure > Active Directory > Enterprise Apps > Your App > Properties
Now you just need bearer authentication token in the header for Microsoft Graph and the right permissions, I think Read/write all directory data should be good enough.

Azure Active Directory Web Application without login current user

I am following an older tutorial (https://code.msdn.microsoft.com/Write-Sample-App-for-79e55502) that shows how to create a web application that connects to an Azure Active Directory tenant using ADAL. There is a class library that contains DirectoryService classes that I believe does the work in retrieving AD user properties. I am wanting to create a login method for this project for security purposes, and to be able to identify what user is logged in to the application. Currently there is not a signin method that authenticates against AD by entering a username/password, so I am a little puzzled at how the app can retrieve user properties with just the AppId,Domain,and AppSecret in the Web.config without actually having someone login with either their AD creds or redirecting to login.microsoftonline/{tenantId}.....I do not know if I am making sense, but I want to be able to add a login method so a user is forced to login so it gets the claims for that specific user but if I am already using ADAL, can I also incorporate Owin?
There are two parts to your question -
1. How is this app working today, without asking end users to explicitly sign in
This sample is using Client Credential Grant, in which you don't need end users to sign in, but use the identity of an application itself to get the auth token and use it for further operations. As you mention yourself, it just needs AppId, Domain and App Secret from web.config. You can read all about it here - Client Credentials Grant
Related code to acquire token is available in MVCDirectoryGraphSample\Helpers\MVCGraphServiceHelper.cs file
AuthenticationContext authenticationContext = new AuthenticationContext(authString);
ClientCredential clientCred = new ClientCredential(ConfigurationManager.AppSettings["AppPrincipalId"],
2. How to add a login method to force users to sign in to your web application
You can start using something like Authorization Code Grant flow, instead of client credentials. Look here for documentation on different scenarios that Azure AD supports. Your case looks like a web application calling Graph API, described here
Exact code sample to do this is available here - Code Samples

How to configure on-behalf-of authentication in multi-tenant environment?

I have a native client that calls a service I wrote-- that in turn calls the Graph API (using the original caller's credentials).
This is exactly like the 'onbehalfof' sample found here (my code fails the same way as the sample):
https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof
When logging in as a user from the same tenant as the service (tenant A), everything works fine (just like the onbehalf of sample). When logging in as a user from a different tenant (tenant B), I get an exception on this line in the service:
result = await authContext.AcquireTokenAsync(GraphResourceId, clientCred, userAssertion);
(this is line 153 from TodoListController.cs in the onbehalfof sample).
The exception is this:
AADSTS65001: The user or administrator has not consented to use the
application with ID 'de2fb28b-83f8-419d-9b00-3fbce0a60bf4'. Send an
interactive authorization request for this user and resource.\r\nTrace
ID: 6865c420-674a-4adf-a070-3d9b9c500200\r\nCorrelation ID:
7e088563-d7fe-4131-a05c-cbe04dbb2bbd\r\nTimestamp: 2017-03-29
22:56:58Z
The application id above refers to the service I wrote (which is the same line in the TodoListService in the onbehalfofsample).
I configured everything for multi-tenant authentication. But it's the additional call that my service is making to another service (Graph API) that's causing the problem. What additional configuration do I need to do in the Azure portal to make this work?
In the instructions written for the sample you linked above, they address this issue with the following section:
Configure known client applications
For the middle tier web API to be able to call the downstream web API,
the user must grant the middle tier permission to do so in the form of
consent. Because the middle tier has no interactive UI of its own, you
need to explicitly bind the client app registration in Azure AD with
the registration for the web API, which merges the consent required by
both the client & middle tier into a single dialog. You can do so by
adding the "Client ID" of the client app, to the manifest of the web
API in the knownClientApplications property. Here's how:
Navigate to your 'TodoListService' app registration, and open the manifest editor.
In the manifest, locate the knownClientApplications array property, and add the Client ID of your client application as an element.
Your code should look like the following after you're done:
"knownClientApplications": ["94da0930-763f-45c7-8d26-04d5938baab2"]
Save the TodoListService manifest by clicking the "Save" button.
My assumption is that because you are running into this problem, that you have not done this special configuration.
The other option you have is to explicitly request consent between the middle tier and the AAD Graph API. You can do this by having a tenant administrator 'login' and consent to your middle tier service. All you need to do is generate a login url with the middle tier App ID.
However, I strongly recommend you do it the documented way, since this will provide a better experience for your users.
It's working now. I had to make two changes to get it working.
First, on the service side switch to using "common" as the tenant. I had switched to common on the client but didn't realize you had to do this on the service side as well:
<add key="ida:Tenant" value="common" />
Second, change the GraphUserUrl on the service to the following URL:
<add key="ida:GraphUserUrl" value="https://graph.windows.net/me?api-version=1.6" />
The original URL in the sample didn't work (at least for users in another tenant).
Here's the consent dialog that appears when a user from another tenant logs in:
Consent Dialog
Here's my client manifest...
Client Manifest
...and my service manifest...
Service Manifest

Resources