This sample demonstrates how to configure ASP.NET application for:
- Multitenant authentication (based on Change your ASP.NET Core Web app to sign-in users in any org with the Microsoft identity platform)
- Swagger client application access using signed-in user
- Application only permissions (based on Get access without a user and A .NET Core daemon console application calling a protected Web API with its own identity)
- Use multiple authentication schemes: AzureAD, Google and Cookie-based
- Get Microsoft/Google access tokens programatically for automated testing
- Microsoft Identity to manage users and roles.
- Sign in to the Azure portal using either a work or school account or a personal Microsoft account
- Register the service web application
- In the Supported account types section, select Accounts in any organizational directory
- In the Expose an API make sure Application ID URI contains "api://[client id]"
- in the Expose an API add scope "api://[client id]/[Application name]"
- In the App roles add new access_as_application role for Applications as Allowed member types
- In the API permissions add Azure Service Management and select user_impersonation
- Register swagger client application
- Add https://localhost:44321 to Authentication/Redirect URIs
- In the Authentication/Implicit grant and hybrid flows section, check ID tokens
- In the Certificates & secrets generate new client secret and save the value
- In the API permissions add My APIs, select service application from step 2 and choose Delegated permissions
- Register UI client application
- In the Supported account types section, select Accounts in any organizational directory
- in the Redirect URIs select Single-page application (SPA) and add https://localhost:44321
- In the API permissions add My APIs, select service application from step 2 and choose Delegated permissions
- Get back to service web application Azure configuration from step 2 and add this UI client application id to Expose an API/Authorized client applications
- Register daemon client application
- In the Certificates & secrets generate new client secret and save the value
- In the API permissions add My APIs, select service application from step 2, choose Application permissions and access_as_application.
- At this stage permissions are assigned correctly but the client app does not allow interaction. Therefore no consent can be presented via a UI and accepted to use the service app. Click the Grant/revoke admin consent for {tenant} button, and then select Yes when you are asked if you want to grant consent for the requested permissions for all account in the tenant. You need to be an Azure AD tenant admin to do this.
- Fill in appsettings.json
- Fill in react-spa\src\authConfig.js
- Start server application by running dotnet run in AspNetAzureSample.WebApi (running on http://localhost:5000 by default).
- Start client application by running npm start in react-spa (running on http://localhost:3000 by default).
- Sign in to the Google Console
- In APIs & Services/Credentials create new credentials
- In the appsettings.json set Google:Enable to true and fill in Google:ClientId.
- Configure necessary Javascript origins and redirect URIs, including https://developers.google.com/oauthplayground
- Go to OAuth2 Playground
- Press top right settings (gear) icon (OAuth 2.0 configuration)
- Tick Use your own OAuth credentials and enter OAuth Client ID and OAuth Client secret
- At the bottom enter scopes:
openid https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile
- Press Authorize APIs.
- On the next screen choose the account (optional screen) and give the permissions to the app.
- Press Exchange authorization code for tokens
- Get your non-expiring refresh_token and put it into appsettings.json
- To get an id_token using refresh_token:
curl -d "client_id=YOUR_APP_CLIENT_ID&client_secret=YOUR_APP_CLIENT_SECRET&grant_type=refresh_token&refresh_token=YOUR_APP_REFRESH_TOKEN" "https://oauth2.googleapis.com/token"
- Open http://localhost:5000/swagger/index.html
- Click Authorize using accounts from different tenants.
- In Postman get an access token as described in Get an access token
- POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
- client_id={daemon app client id from step 4}
- scope={service app client id from step2}/.default
- client_secret={daemon app client secret from step 4}
- grant_type=client_credentials
- Using generated token access https://localhost:5000/Maintenance
- Open http://localhost:3000/
- Click Login and choose one of the supported options:
- Sign in with Google Access Token
- Google one tap
- Microsoft
- Click Get Weather button. It will send the request to server with corresponding bearer token.
- Click Profile to see your profile's information retrieved from the corresponding identity provider.
In certain scenarios, it may be necessary to run the application with authentication enabled but without using a real user, especially during testing. This sample includes a special middleware that ensures the return of an authenticated user without actual authentication. This middleware is only added when the ASPNETCORE_ENVIRONMENT
is set to Testing
.
You can initiate the testing
profile from Visual Studio or by running the following command:
dotnet run --environment Testing
When running in this mode, attempting to access the API without proper authentication in Swagger or a client will be restricted. However, you can still access the API by including the header X-Testing-Name=<username>
in the request, for instance, when using Postman at https://localhost:44321/WeatherForecast.
- Open Swagger UI via http://localhost:5000/swagger/index.html
- Click Authorize button and authenticate with your Microsoft credentials
- Confirm you can execute http://localhost:5000/WeatherForecast. In this case Bearer authentication scheme is challenged.
dbug: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[8]
AuthenticationScheme: Bearer was successfully authenticated.
info: AspNetAzureSample.UserProviders.DefaultUserProvider[0]
DefaultUserProvider: received '' user name.
- Click Authorize button again and click Logout
- Confirm you cannot anymore execute http://localhost:5000/WeatherForecast and get
401 Error: Unauthorized
- Execute
/register
with body:
{
"email": "alice@gmail.com",
"password": "string1!"
}
You can also use hardcoded users:
admin@example.com P@ssword1
alice@example.com P@ssword1
bob@example.com P@ssword1
- Execute
/login
with the same body. SetuseCookies
totrue
. New cookie will appear in the browser, a sample cookie record is shown: - Confirm you can again execute http://localhost:5000/WeatherForecast. In this case Identity.BearerAndApplication authentication scheme is challenged.
dbug: Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions+CompositeIdentityHandler[8]
AuthenticationScheme: Identity.BearerAndApplication was successfully authenticated.
info: AspNetAzureSample.UserProviders.DefaultUserProvider[0]
DefaultUserProvider: received 'alice@gmail.com' user name.
If you remove the cookie, authentication will again fail.
You can switch between storage types by Storage:StorageType property in the appsettings.json:
- InMemory
- MySql
- SqlServer
To run MySql docker image:
docker run -d --name mysql-container -e MYSQL_ROOT_PASSWORD=my-secret-pw -e MYSQL_DATABASE=TweetsDb -e MYSQL_USER=myuser -e MYSQL_PASSWORD=mypassword -p 3306:3306 mysql:latest
If you want to switch between different databases:
dotnet ef migrations remove
dotnet ef migrations add InitialCreate
dotnet ef database update
3 dummy users are seeded to easily test solution:
admin@example.com P@ssword1
alice@example.com P@ssword1
bob@example.com P@ssword1