Consume Secure Azure API from SharePoint online - azure

I have developed Azure API app with authentication on feature, log in with Azure Active Directory, I need to consume this API from SharePoint online , I
I need to authenticate and consume the azure API, no signin-prompt, every thing should be handled in the script
need to use ADAL.js to authenticate secure API ,I cannot find any good reference about the JavaScript code, I was wondering if anyone have a good reference how the JavaScript code should look like?
Thanks!

Here are the steps to call the azure hosted API from SharePoint online using JavaScript and ADAL.js library, no signing-prompt, everything should be handled in the script using ADAL.js to authenticate secure API
Create and Configure API
Create azure API  
Publish your azure API in azure
Browse to azure portal, select your API application , select Authentication/Authorizations
Set the App Service Authentication: On
Action to take when request us not authenticated: Log in with Azure dictionary
Authentication providers: Express
Now the API is protected with Azure AD, if you navigate your API via browser you will be prompted for login
When we set authentication in Express mode, the app will be created automatically in Azure Active directory, you can see the name under Azure AD app
Navigate to Azure management portal, click on active directory in left navigation,
Click on the Directory which will be federated to your office 365 (or any source you want to call azure API which uses the same azure active directory as your configured for you API authentication)
Click on the Application, and you will find you AD app in the list which has been created with Express method as we discussed om step nr.3
Now we need to create new app in AAD which will be our communication channel from office 365 to Azure API, Click on ADD on footer
Enter name and select “WEB APPLICATION AND/OR WEB AP” option
For Sign in URL enter your SharePoint online Url which you are planning to call Azure API from
For APP ID URL, enter unique Url, this will be used as a unique logical identifier for your app.
After the app created, click on Configure, copy the Client ID which will be used later­­
Under Permission to other applications, Click “Add Application”, on the next page select “All Apps” and select you Azure API app which you double checked in step nr.8, and confirm
You will be redirected back to Configure page, Under Permission to other applications, now you see your azure API app is listed here, click on delegated permission, select the access to app
At the bottom of the page, click Manage manifest > Download manifest.
Download the file to a location where you can edit it.
In the downloaded manifest file, search for the oauth2AllowImplicitFlow property. Change the value of this property from false to true, and then save the file.
Click Manage manifest > Upload manifest, and upload the file that you updated in the preceding step.
Azure management portal, select setting and copy your AAD subscription ID for related AAD
Call Azure API from SharePoint Online
Once the above steps have been done, you can call azure API from sharePoint online which is using same Active directory above
Edit the page and add Script Editor web part
Add following script
· subscriptionId, see step nr.20
· clientId see step nr.13
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.13/js/adal.min.js"></script>
<script type="text/javascript">
function CallAzureAPI() {
"use strict";
var subscriptionId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
var clientId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
window.config = {
subscriptionId: subscriptionId,
clientId: clientId,
postLogoutRedirectUri: window.location.origin,
endpoints: {
AzureApiUri: 'https://xxxxxxxxxxxx.azurewebsites.net'
},
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);
}
// If not logged in force login
var user = authContext.getCachedUser();
if (user) {
// Logged in already
console.log(user);
}
else {
authContext.login();
}
// Acquire token for Files resource.
authContext.acquireToken(config.endpoints. AzureApiUri, function (error, token) {
// Handle ADAL Errors.
if (error || !token) {
console.log('ADAL error occurred: ' + error);
return;
}
var ApiUri = "https://xxxxxxxxx.azurewebsites.net/api/Get";
$.ajax({
type: "GET",
url: ApiUri,
headers: {
'Authorization': 'Bearer ' + token,
}
}).done(function (response) {
console.log('Successfully called API.');
console.log(response);
}).fail(function () {
console.log('Calling API failed.');
});
});
}
</script>
<input type='button' value='Call Azure API' onclick=" CallAzureAPI ();"/>
This solution works though after some time ( later I found out when AAD cookie is expired ) we get this error "Token renewal operation failed due to timeout ",
I did some research and I found out he getCachedUser or getUser methods look into the browser storage for id_token and returns a non-null user if there is a token inside the cache. It does not look into the token expiration time though.
What's happening here is since localStorage is used, tokens are preserved in the cache when one re-opens the browser (and hence getCachedUser returns a non-null object) but the AAD cookie is expired (unless user checked the keep me signed in checkbox when logging in). Since the cookie is expired, acquire token call fails with the "login required" error.
so as workaround I checked , keep me signed in checkbox when logging in and it works .

It is possible to call the web API which protected by Azure AD from the SharePoint online using the JavaScript, however it is very complex.
Here are the steps for your reference:
Developing the web API which protected by Azure AD
register an native app on the same tenant
enable the implicit flow for the native app
grant the native app to access the web API from Azure portal
using the admin_consent for the native app to grant permission fo the organization
developing a web page in web API project for the redrect page for the OAuth 2.0 request
write the code in the web page using windows.postMessage to post the token to the parent page
$().ready(function () {
if (window.parent != null) {
// get the token from URL here
var token = "123";
console.log(window.location);
window.parent.postMessage(token, "*");
}
})
In SharePoint online page, using iframe to send the implict flow like code below
<iframe id="iframe_id" src="https://login.microsoftonline.com/{tenanit}.onmicrosoft.com/oauth2/authorize?response_type=token&client_id={clientId}&resource={webApiAppIdUri}&redirect_uri={redirect_uri}&prompt=none"></iframe>
<script>
var token = "";
window.addEventListener("message", receiveMessage, false);
function receiveMessage(event) {
token=event.data;
console.log(event.data);
}
</script>
Here is a figure to help understanding the progress:

Related

Getting Error AADB2C99067 when trying to request access token from Azure B2C

I have an Azure AD B2C tenant setup with an Angular app on the front-end using Authorization Code Flow with PKCE and a back-end api. Everything is working fine. I now have a need to allow the user to access certain pages on the front-end anonymously. I would prefer to still protect the apis these pages will call using the same access token.
I have followed the article here to set up Client Credentials flow. I am able to get an access token successfully using Postman and use it to call my back-end apis fine. However, when I try to do the same from the Angular app, I get the following error:
{"error":"invalid_request","error_description":"AADB2C99067: Public Client XXXXX-XXXXXX is not supported for Client Credentials Grant Flow\r\nCorrelation ID: 2b3346ef-1828-4900-b890-06cdb8e0bb52\r\nTimestamp: 2022-07-28 04:12:21Z\r\n"}
Below is the code snippet I am using in Angular to retrieve the access token.
const urlencoded = new URLSearchParams();
urlencoded.set('grant_type', 'client_credentials');
urlencoded.set('client_id', '<clientid>');
urlencoded.set('client_secret', '<clientsecret>');
urlencoded.set('scope', '<scope>');
const httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded' }),
};
const url = 'https://<b2ctenant>.b2clogin.com/<b2ctenant>.onmicrosoft.com/<customPolicy>/oauth2/v2.0/token';
return this.httpClient.post(url, urlencoded, httpOptions);
Any ideas what could be missing?
Thanks!
Though azureadb2c supports client_credential flow.One may not use them with SPA apps.
This scenario is not supported by MSAL.js. Client credential flow/ grant type will not work in SPAs(Angular) because browsers cannot securely keep client secrets.
As they may end up in the browser, visible to everyone and to attackers that load them.
Note:As the application's own credentials itself are being used, they must be kept safe - never publish that credential in your source
code
If you are using it for web app , please make sure to select the platform as web or change the reply url type to be web.
"replyUrlsWithType": [
{
"url": "https......",
"type": "Web"
},
]
Please refer :
Configure authentication in a sample Angular SPA by using Azure
Active Directory B2C | Microsoft Docs
OAuth 2.0 client credentials flow on the Microsoft identity platform- Microsoft Entra | Microsoft Docs

Solved: Got Request_ResourceNotFound and Tenant does not have a SPO license when calling Microsoft Graph API

I am ultimately trying add data to an Excel file in my OneDrive using Graph API with a local node.js app running on my PC, not a website, and I'm aiming to simply call 'node index.js' in console to do this, without having to authorise my login (perhaps this is impossible?)
I'm starting off with these endpoints to get simple data first but can't get them to work (base URL https://graph.microsoft.com):
/v1.0/me/
/v1.0/users/{My-UserID}/
/v1.0/me/drive/root:/TestFile.xlsx:/workbook/worksheets
/v1.0/users/{My-UserID}/drive/root:/TestFile.xlsx:/workbook/worksheets
/v1.0/me/ and /v1.0/users/{My-UserID}/ returns 'Resource \'{My-UserID}\' does not exist or one of its queried reference-property objects are not present.'
/v1.0/me/drive/root:/TestFile.xlsx:/workbook/worksheets and /v1.0/users/{My-UserID}/drive/root:/TestFile.xlsx:/workbook/worksheets returns 'Tenant does not have a SPO license.'.
How I got my token
I got the access token using the adal-node library (https://www.npmjs.com/package/adal-node), using the 'Server to Server via Client Credentials' flow.
How I called the graph API
I am calling the API manually without the the Microsoft Graph SDK using axios.
How I registered my app in Azure portal
I went to 'Azure Active Directory'.
Clicked 'App registrations'.
Clicked 'New registration'.
a) Chose an app name.
b) Selected 'Accounts in this organizational directory only (Default Directory only - Single tenant)' in 'Supported account types'.
c) Left 'Redirect URI (optional)' empty
Clicked 'Register'.
Copied 'Application (client) ID' and used it as the 'ClientID' constant in my app below.
Copied 'Directory (tenant) ID' and used it as the 'Tenant' constant in my app below.
Clicked 'Certificates & secrets'.
Clicked 'New client secret'.
a) Left 'Description' empty.
b) Chose 'Never' under 'Expires'.
c) Clicked 'Add'.
Copied the generated secret and used it as the 'ClientSecret' constant in my app below.
Clicked 'API permissions'.
Clicked 'Add a permission'.
Clicked 'Microsoft Graph'.
Clicked 'Application permissions'.
Checked 'Files.Read.All'.
Clicked 'Add permissions'.
Clicked 'Grant admin consent for Default Directory', clicked 'Yes'.
I have also tried adding delegated permissions checking all permissions available and application permissions for 'Directory.Read.All' and 'Group.Read.All'.
What I've tried
I have tried using Graph Explorer (https://developer.microsoft.com/en-us/graph/graph-explorer) and can see that both
/v1.0/me/ and /v1.0/me/drive/root:/TestFile.xlsx:/workbook/worksheets returns correct data.
I have also tried replacing /me/ with /users/{My-UserID}/ and Graph Explorer returns the same correct data but my node.js app still returns the same errors.
I used a JWT decoder to check my retrieved token and seems like
oid correctly equals to {My-UserID}.
roles included 'Files.Read.All' and 'User.Read.All' which is as I set it up in my app registration in Azure portal.
appid correctly equals to {My-ApplicationID}.
tid correctly equals to {My-TenantID}.
aud equals 'https://graph.microsoft.com'
I also tried obtaining the token manually by calling POST to https://login.microsoftonline.com/{My-TenantID}/oauth2/v2.0/token with header:
'Content-Type': 'application/x-www-form-urlencoded'
and data:
client_id=${My-ClientID}&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret={My-ClientSecret}&grant_type=client_credentials
and got this error: 'AADSTS7000215: Invalid client secret is provided'. <-- Edit: I found out why I'm getting this error. I had to escape special characters like : / + using %{ascii-code-in-hex}.
Other things to note
I have an Office 365 home subscription and a pay-as-you-go Azure account using the same email address as my OneDrive that has the Excel file that I want to add data to.
Edit: I found out that if you use Graph Explorer and call /v1.0/users/{any-number} you will get back your own profile. This means that {My-UserID} could have not been my correct user ID all along.
const Tenant = {My-TenantID};
const ClientID = {My-ApplicationID};
const ClientSecret = {My-ClientSecret};
function GetAccessToken()
{
const AuthenticationContext = require('adal-node').AuthenticationContext;
const authorityUrl = `https://login.microsoftonline.com/${Tenant}`;
const applicationId = ClientID;
const clientSecret = ClientSecret;
const resource = 'https://graph.microsoft.com';
const context = new AuthenticationContext(authorityUrl);
return new Promise((resolve, reject) =>
{
context.acquireTokenWithClientCredentials(resource, applicationId, clientSecret, (error, tokenResponse) =>
{
if (error)
{
console.log('Get token error:');
console.dir(error.stack, {depth: null});
reject(error);
}
else
{
console.log('Get token success:');
console.dir(tokenResponse, {depth: null});
resolve(tokenResponse.accessToken);
}
});
});
}
async function GetData()
{
const axios = require('axios');
const AccessToken = await GetAccessToken();
axios(
{
method: 'GET',
url: 'https://graph.microsoft.com/v1.0/me/', //I have also tried /v1.0/users/{My-UserID}/
headers:
{
'Authorization': `Bearer ${AccessToken}`,
}
})
.then(result =>
{
console.log('Get data success:');
console.dir(result.data, {depth: null});
})
.catch(error =>
{
console.log('Get data failed:');
if (error.response.data != undefined)
{
console.dir(error.response.data, {depth: null});
}
else
{
console.dir(error, {depth: null});
}
})
}
GetData();
Update 1
I think I've figured out why /v1.0/me nor /v1.0/users/{My-UserID} does not work. If we authenticate without a user (the flow I'm opting for here), the only users the application will be able to access are the ones in Azure Active Directory (AAD). Now, if you login to Azure using your hotmail account and go to 'Azure Active Directory' then 'Users' you will see your account there but I think that that account is actually a different account to your 'common' Microsoft account ('common' Microsoft account being the one you signed up with hotmail). Yes, it seems that Microsoft creates a separate 'AAD' identity and this is a different user than your 'common' identity user.
{My-UserID} is actually the user ID for the 'common' identity. So..this means that if I call /v1.0/users/{My-UserID}, my application will not recognise that user ID, because that user ID does not belong in the AAD. So what I tried is copying the 'Object ID' from the user in AAD, and used that instead: /v1.0/users/{AAD-ObjectID} then viola, I see my profile.
Edit: {My-UserID} is actually NOT my 'common' identity's user ID. It is some other ID that I don't know yet.
I then tried:
creating a new application but instead of choosing 'Accounts in this organizational directory only (Default Directory only - Single tenant)' in 'Supported account types', choose 'Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)' instead
followed the 'Get access on behalf of a user' auth flow
and can confirm that with this flow, /v1.0/me and v1.0/users/{My-UserID} does work. Edit: Graph API actually returns my profile no matter what ID you specify, so you can specify v1.0/users/123 and it will still return your profile!
So I thought, hmm, how about instead of granting admin consent from Azure portal, I grant admin consent using the https://login.microsoftonline.com/common/adminconsent endpoint. Doing this, and getting access token from /common/oauth2/v2.0/token then
/v1.0/me returns 'Current authenticated context is not valid for this request.'
/v1.0/users/{My-UserID} returns 'The identity of the calling application could not be established' error.
/v1.0/users/{AAD-ObjectID} returns 'The identity of the calling application could not be established' error.
If I get access token from /{My-TenantID}/oauth2/v2.0/token then
/v1.0/me and /v1.0/users/{My-UserID} returns original 'Request_ResourceNotFound' error.
/v1.0/users/{AAD-ObjectID} correctly returns my profile.
I then tried /v1.0/users/{AAD-ObjectID}/drive/root:/TestFile.xlsx:/workbook/worksheets but am still getting 'Tenant does not have a SPO license.' I suspect this is because my AAD identity does not have the necessary license to manipulate OneDrive items.
A workaround for adding data to Excel without going down the 'Get access on behalf of user' auth flow that I found works is using Logic App instead. So in a Logic App, I set up an HTTP request endpoint which when triggered will add data to Excel and get my Node.js app to call this endpoint instead of calling Graph API directly.
In conclusion
I think this is probably something that Microsoft have to fix on their end - the AAD identity shouldn't be a separate identity but the same identity as my 'common' identity so that I can access my user info using /v1.0/users/{My-UserID} and manipulate my OneDrive files using /v1.0/users/{My-UserID}/drive.
Update 2
I have found a way to call Graph API directly without using Logic App!
So for those who have followed my problem, there are 2 ways call a Graph API:
Call on behalf of a user
Call without a user
The key point I figured out is 'call on behalf of a user' is like saying, you, the user, wants to use Graph API through your app, which is actually what I want because I want to call Graph API to edit my Excel file. 'Call without a user' is like saying, the app itself is calling Graph API - not the user using the app to call Graph API, so it won't have access to user data, unless that user is in the active directory. It's important to note (from my previous answer) here that the user you see in AAD that has the same name as you is not actually 'you', it is the 'AAD' version of you. So even though you see your name in the AAD, 'Call without a user' will not be able to access your OneDrive items. You'll need 'Call on behalf of a user' for that.
Now if we need to do 'Call on behalf of a user', I thought I would have to go through the 'get authorization code --> get access token' every time to access my OneDrive files, but I just found out that you can also refresh the token so you can keep accessing the OneDrive files without having to get a new authorization code. The most important thing for this step is to make sure to include offline_access to the scope parameter when getting the authorization code and getting the token, otherwise refreshToken will not be returned when you ask for the token and you will not be able to refresh the token. You will also need to add offline_access delegated permission in your app registration as well.
So with this, I can just get my node app to save the refreshToken and refresh the token every time it runs and viola, I can edit my Excel file whenever I want!
You cannot use /me with Client Credentials. Since you're not authenticating a user, Graph has no way of determining which user "me" should map to.
When using an App-Only token (i.e. Client Credentials), you need to specify the user you want address:
/v1.0/users/{id or userPrincipalName}
/v1.0/{id or userPrincipalName}/drive/root:/TestFile.xlsx:/workbook/worksheets

How to Login and Authenticate Microsoft Graph B2C User across ASP Net Core2, VueJS, Xamarin.Forms and Azure Functions C# precompiled (v1 and v2)?

I am developing a portal, and have this scenario:
The user enters the ASP.Net Core 2 Razor Pages Web portal, signup or sign-in with Facebook
On the main page, index.cshtml, there is HTML and a simple VueJS with a property {{ Account.Total }}
The VueJS brings this Account.Total value from an Azure Function using Axios Javascript library. https://myfunctionapp.azurewebsites.net/api/GetAccountTotal?AccountId=ABC
User also logs in UWP and Mobile Xamarin.Forms App
The Mobile App, made in Xamarin.Forms also calls this Azure Function to get the Account.Total
The UWP App also calls this Azure Function to get the Account.Total
The idea is that the ASP.Net Core would be just a Client, like the UWP, and Xamarin App. The Azure Functions would be the backend, like the Web API; The Microsoft Graph Facebook Authentication would secure all this and identify the User.
The Azure Function need to use the same Graph/Azure AD B2C Authentication. The user will login just one time at the ASP.Net Razor Page, and all related services and calls must read these credentials.
How to configure, and what code is needed to do this Login and Authenticate Microsoft Graph B2C Facebook User Account across ASP Net Core2, VueJS and Azure Functions C# precompiled (v1 and v2)?
All this look obvious, but after some days trying, I will post here the steps.
(WIll edit this answer to put more details)
But the most important difficult to find part (in order) are:
1) It worked after I added the additionalLoginParams
"additionalLoginParams": ["response_type=code id_token", "resource=<app_id>"]
on https://resources.azure.com/
At the top of the page, select Read/Write.
In the left browser, navigate to subscriptions > resourceGroups > > providers > Microsoft.Web > sites > > config > authsettings.
Click Edit.
Modify the following property. Replace with the Azure Active Directory application ID of the service you want to access.
2) On ASP.Net Core 2 you put Login and Logout:
LOGIN<br />
LOGOUT<br />
and you can request
string UserName = (string)Request.Headers["X-MS-CLIENT-PRINCIPAL-NAME"];
string UserId = (string)Request.Headers["X-MS-CLIENT-PRINCIPAL-ID"];
string Token = Request.Headers["X-MS-TOKEN-AAD-ID-TOKEN"];
3) Then on the JavaScript, you must Call the Azure Function sending the Authorization Header Bearer Token
function WhoAmI() {
var Token = "#ViewData["Token"]";
var url = "https://MyFUNCTION.azurewebsites.net/api/WhoAmI";
var headers = { headers: { "Authorization": `Bearer ${Token}` } };
//if (Token == "") headers = null;
axios.get(url,
headers
).then(response => {
console.log("WhoAmI()");
console.log(response.data);
});
}
Sources:
https://learn.microsoft.com/en-us/azure/app-service/app-service-authentication-how-to#refresh-access-tokens
Maybe I will also need to use
0) ADAL.NET (Microsoft.IdentityModel.Clients.ActiveDirectory)

Azure active directory v2.0 query for Web API integration on a SharePoint site

We have a SharePoint publishing site with anonymous access hosted on the internet. As per out latest requirements, we need to implement user login (AzureAD, Microsoft personal and work accounts, and more).
https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-flows
As per the documentation here, we want to implement this using Web API to get the secure information from the database. We are thinking about using MSAL.js file for user login and logout on the SharePoint and after getting a bearer token we can call the Web API for the additional data from our database.
Standalone Web APIs restriction: “You can use the v2.0 endpoint to build a Web API that is secured with OAuth 2.0. However, that Web API can receive tokens only from an application that has the same Application ID. You cannot access a Web API from a client that has a different Application ID. The client won't be able to request or obtain permissions to your Web API.”
How can we create two applications with same application ID at App Registration Portal? Or should we use the same application ID at SharePoint and Web API’s end?
There is no need to register two application, you only need to one register application. After you register the application, you can using the MSAL library below to get the token to call the web API:
<script class="pre">
var userAgentApplication = new Msal.UserAgentApplication("e5e5f2d3-4f6a-461d-b515-efd11d50c338", null, function (errorDes, token, error, tokenType) {
// this callback is called after loginRedirect OR acquireTokenRedirect (not used for loginPopup/aquireTokenPopup)
})
userAgentApplication.loginPopup(["user.read"]).then(function (token) {
var user = userAgentApplication.getUser();
console.log(token);
// signin successful
}, function (error) {
// handle error
});
</script>
And to protect the web API, you can use the same app and refer the code below:
public void ConfigureAuth(IAppBuilder app)
{
var tvps = new TokenValidationParameters
{
// The web app and the service are sharing the same clientId
ValidAudience = "e5e5f2d3-4f6a-461d-b515-efd11d50c338",
ValidateIssuer = false,
};
// NOTE: The usual WindowsAzureActiveDirectoryBearerAuthenticaitonMiddleware uses a
// metadata endpoint which is not supported by the v2.0 endpoint. Instead, this
// OpenIdConenctCachingSecurityTokenProvider can be used to fetch & use the OpenIdConnect
// metadata document.
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider("https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration")),
});
}

Azure AD OpenIDConnect + ASP.NET Core - Authenticate and Extra Permissions/Token?

I am using the following bits against my Azure AD to authenticate with ASP.NET Core.
https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapp-openidconnect-aspnetcore/
https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect-aspnetcore
I have the basic login/auth working after creating an Azure AD app. User can login/logout.
My question is given this, what's the best way when a user Auth's to log to a DB? I thought about making the redirect URL to an endpoint, saving, then just redirecting back to "Home" but is that ideal?
Also, is it possible to retrieve a bearer token via this approach? Or does this require another type of call or extending "scope"? So that for example I could retrieve the authenticated users Manager.
https://graph.microsoft.com/v1.0/me/manager
My question is given this, what's the best way when a user Auth's to log to a DB? I thought about making the redirect URL to an endpoint, saving, then just redirecting back to "Home" but is that ideal?
This way only able to log those who already sign-in your app successfully. It is not able to log those users who are attempt to sign-in your app but enter the wrong password.
Azure AD already provide lots of report to gain visibility into the integrity and security of your organization’s directory.( refer here)
And if you are using the Azure AD Premium, you can review the sign-in activities via the Azure new portal below:
And if you want to store the sign-in activity in your web app, you can write the custom code after the token is verified. Here is the code for your reference:
// Configure the OWIN pipeline to use OpenID Connect auth.
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = Configuration["AzureAD:ClientId"],
Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAd:Tenant"]),
ResponseType = OpenIdConnectResponseType.IdToken,
PostLogoutRedirectUri = Configuration["AzureAd:PostLogoutRedirectUri"],
Events = new OpenIdConnectEvents
{
OnRemoteFailure = OnAuthenticationFailed,
OnTokenValidated = context => {
//write the custom code to store users login-in
return Task.FromResult(0); }
},
});
Also, is it possible to retrieve a bearer token via this approach?
Yes. We can get the token after receive the authorization code. You can refer the code sample here to acquire the token from asp.net core app.

Resources