Reputation: 101
I have a Blazor Server App that uses Microsoft Identity with local user accounts. It works perfectly; however, I want to extend the app to have access to Office 365, namely mail, calendar and a sharepoint document library. This can be done via Microsoft Graph, but all the documentation points to use Microsoft Identity with Work or School accounts instead of local user accounts. I do not want to change the Blazor app since I will add also support for other cloud services. The main question is can I leave my Blazor app with Microsoft Identity and use MSAL or something else with "on behalf of user" access in addition, capturing or asking the user to enter, once, his/her credentials for office 365 and thus gain access?
The goal is to have the blazor server side app running with local account and prompt once for the user's Office365 login, get the access and retrieve the data. Next time the user logs into the app, he won't need to login to Office365 for the app to have access to the email, calendar and a sharepoint site as the user.
Upvotes: 0
Views: 1304
Reputation: 793
Yes, I came to a similar conclusion and implemented an almost identical solution - wish I had seen this post first though.
Pretty disappointing that Microsoft.Identity.Web doesn't seem to support Individual Accounts - I felt a bit exposed having to implement something as important as authentication effectively from first principles.
There remain challenges with this approach if you want to move to Blazor WASM. The secure caching of the access token has been my concern on client side. I wasted a lot of time trying to work out how to integrate Azure authentication using current libraries with Indiv Accounts. (Note I wanted to make this a comment, but I don't have enough reputation on SO)
Upvotes: 0
Reputation: 101
After battling with ADAL, MSAL, and other wrappers for OAUT2 authentication for Azure, I came to a simple solution which reminded me of the KISS (Keep It Simple S!"#$%) aspect. Sometimes we overthink the problems and it was my situation.
After registering the application in Azure AD, make sure your redirect URI matches an application page, component, or method of capturing the authentication code. I used the following code (this is in a Razor component since I use a Blazor Server App):
string AuthUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
string ReqUrl = AuthUrl + "?scope=";
ReqUrl += string.Join("+", scopes);
ReqUrl += "&response_type=code";
ReqUrl += "&client_id=" + [Replace these brackets with your Client ID];
ReqUrl += "&redirect_uri=[Replace these brackets with your redirect URI for your app page]";
ReqUrl += "&prompt=consent";
ReqUrl += "&response_mode=query";
NavManager.NavigateTo(ReqUrl);
Since this code navigates to the Azure AD Authentication, if successfully authenticated it redirects to another razor component in my Blazor Server App to capture the authorization code in the query string which can be easily extracted using the System.Web code below:
QueryHelpers.ParseQuery(uri.Query).TryGetValue("code", out [Replace with Code Variable])
You can then acquire an access token, simply again using the same process as authentication, with Post instead of Get:
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("client_id", [Replace with CLient ID]),
new KeyValuePair<string, string>("scope", string.Join(" ", scopes)),
new KeyValuePair<string, string>("grant_type", "[Insert Authorization Code you got in the query string]"),
new KeyValuePair<string, string>("code", HttpUtility.UrlEncode([Replace with Authentication Code])),
new KeyValuePair<string, string>("redirect_uri", "[Redirect URI from App Registration]")
}
);
HttpResponseMessage response = await client.PostAsync("/common/oauth2/v2.0/token", content);
var str = response.Content.ReadAsStringAsync().Result;
Another important aspect of this access code PostAsync is that scopes are delimited with a space and NOT a plus "+" sign as in authentication.
From here, the only caveat of not using a library, is that you must implement your own token cache and renewal. The response from the access token acquisition will return the access token, expiration date and refresh token. I used this Archived Lab Manual of how to get an access token to guide me and to replicate it in C#/Blazor.
I hope this helps anyone else that has been wanting to achieve the same goal, that is to have Local Accounts in a Blazor Server App that is linked to an Office 365 account for access to mail and other resources through MS Graph. Feel free to send a message if anything needs further clarification.
Happy Coding!
Upvotes: 1