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!