In ASP.NET Core, for Microsoft identity platform applications, the Sign in button is exposed in Views\Shared\_LoginPartial.cshtml (for an MVC app) or Pages\Shared\_LoginPartial.cshtm (for a Razor app). It's displayed only when the user isn't authenticated. That is, it's displayed when the user hasn't yet signed in or has signed out. On the contrary, The Sign out button is displayed when the user is already signed-in. Note that the Account controller is defined in the Microsoft.Identity.Web.UI NuGet package, in the Area named MicrosoftIdentity
In ASP.NET MVC, the Sign in button is exposed in Views\Shared\_LoginPartial.cshtml. It's displayed only when the user isn't authenticated. That is, it's displayed when the user hasn't yet signed in or has signed out.
When an unauthenticated user visits the home page, the index route in app.py redirects the user to the login route.
@app.route("/")
def index():
if not (app.config["CLIENT_ID"] and app.config["CLIENT_SECRET"]):
# This check is not strictly necessary.
# You can remove this check from your production code.
return render_template('config_error.html')
if not auth.get_user():
return redirect(url_for("login"))
return render_template('index.html', user=auth.get_user(), version=identity.__version__)
The login route figures out the appropriate auth_uri and renders the login.html template.
@app.route("/login")
def login():
return render_template("login.html", version=identity.__version__, **auth.log_in(
scopes=app_config.SCOPE, # Have user consent to scopes during log-in
redirect_uri=url_for("auth_response", _external=True), # Optional. If present, this absolute URL must match your app's redirect_uri registered in Azure Portal
))
In ASP.NET, selecting the Sign-in button in the web app triggers the SignIn action on the AccountController controller. In previous versions of the ASP.NET Core templates, the Account controller was embedded with the web app. That's no longer the case because the controller is now part of the Microsoft.Identity.Web.UI NuGet package. See AccountController.cs for details.
This controller also handles the Azure AD B2C applications.
In ASP.NET, sign in is triggered from the SignIn() method on a controller (for instance, AccountController.cs#L16-L23). This method isn't part of the .NET Framework (contrary to what happens in ASP.NET Core). It sends an OpenID sign-in challenge after proposing a redirect URI.
public void SignIn()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
In Java, sign-out is handled by calling the Microsoft identity platform logout endpoint directly and providing the post_logout_redirect_uri value. For details, see AuthPageController.java#L30-L48.
@Controller
public class AuthPageController {
@Autowired
AuthHelper authHelper;
@RequestMapping("/msal4jsample")
public String homepage(){
return "index";
}
@RequestMapping("/msal4jsample/secure/aad")
public ModelAndView securePage(HttpServletRequest httpRequest) throws ParseException {
ModelAndView mav = new ModelAndView("auth_page");
setAccountInfo(mav, httpRequest);
return mav;
}
// More code omitted for simplicity
When the user selects the Sign in link, which triggers the /auth/signin route, the sign-in controller takes over to authenticate the user with Microsoft identity platform.
login(options = {}) {
return async (req, res, next) => {
/**
* MSAL Node library allows you to pass your custom state as state parameter in the Request object.
* The state parameter can also be used to encode information of the app's state before redirect.
* You can pass the user's state in the app, such as the page or view they were on, as input to this parameter.
*/
const state = this.cryptoProvider.base64Encode(
JSON.stringify({
successRedirect: options.successRedirect || '/',
})
);
const authCodeUrlRequestParams = {
state: state,
/**
* By default, MSAL Node will add OIDC scopes to the auth code url request. For more information, visit:
* https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
*/
scopes: options.scopes || [],
redirectUri: options.redirectUri,
};
const authCodeRequestParams = {
state: state,
/**
* By default, MSAL Node will add OIDC scopes to the auth code request. For more information, visit:
* https://docs.microsoft.com/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
*/
scopes: options.scopes || [],
redirectUri: options.redirectUri,
};
/**
* If the current msal configuration does not have cloudDiscoveryMetadata or authorityMetadata, we will
* make a request to the relevant endpoints to retrieve the metadata. This allows MSAL to avoid making
* metadata discovery calls, thereby improving performance of token acquisition process. For more, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/performance.md
*/
if (!this.msalConfig.auth.cloudDiscoveryMetadata || !this.msalConfig.auth.authorityMetadata) {
const [cloudDiscoveryMetadata, authorityMetadata] = await Promise.all([
this.getCloudDiscoveryMetadata(this.msalConfig.auth.authority),
this.getAuthorityMetadata(this.msalConfig.auth.authority)
]);
this.msalConfig.auth.cloudDiscoveryMetadata = JSON.stringify(cloudDiscoveryMetadata);
this.msalConfig.auth.authorityMetadata = JSON.stringify(authorityMetadata);
}
const msalInstance = this.getMsalInstance(this.msalConfig);
// trigger the first leg of auth code flow
return this.redirectToAuthCodeUrl(
authCodeUrlRequestParams,
authCodeRequestParams,
msalInstance
)(req, res, next);
};
}
redirectToAuthCodeUrl(authCodeUrlRequestParams, authCodeRequestParams, msalInstance) {
return async (req, res, next) => {
// Generate PKCE Codes before starting the authorization flow
const { verifier, challenge } = await this.cryptoProvider.generatePkceCodes();
// Set generated PKCE codes and method as session vars
req.session.pkceCodes = {
challengeMethod: 'S256',
verifier: verifier,
challenge: challenge,
};
/**
* By manipulating the request objects below before each request, we can obtain
* auth artifacts with desired claims. For more information, visit:
* https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_node.html#authorizationurlrequest
* https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_node.html#authorizationcoderequest
**/
req.session.authCodeUrlRequest = {
...authCodeUrlRequestParams,
responseMode: msal.ResponseMode.FORM_POST, // recommended for confidential clients
codeChallenge: req.session.pkceCodes.challenge,
codeChallengeMethod: req.session.pkceCodes.challengeMethod,
};
req.session.authCodeRequest = {
...authCodeRequestParams,
code: '',
};
try {
const authCodeUrlResponse = await msalInstance.getAuthCodeUrl(req.session.authCodeUrlRequest);
res.redirect(authCodeUrlResponse);
} catch (error) {
next(error);
}
};
}
/**
* Retrieves cloud discovery metadata from the /discovery/instance endpoint
* @returns
*/
async getCloudDiscoveryMetadata(authority) {
const endpoint = 'https://login.partner.microsoftonline.cn/common/discovery/instance';
try {
const response = await axios.get(endpoint, {
params: {
'api-version': '1.1',
'authorization_endpoint': `${authority}/oauth2/v2.0/authorize`
}
});
return await response.data;
} catch (error) {
throw error;
}
}
When the user selects the Sign in link, they're brought to the Microsoft identity platform authorization endpoint.
A successful sign-in redirects the user to the auth_response route, which completes the sign-in process using auth.complete_login, renders errors if any, and redirects the now authenticated user to the home page.
@app.route(app_config.REDIRECT_PATH)
def auth_response():
result = auth.complete_log_in(request.args)
if "error" in result:
return render_template("auth_error.html", result=result)
return redirect(url_for("index"))
After the user has signed in to your app, you'll want to enable them to sign out.
Sign-out
Signing out from a web app involves more than removing the information about the signed-in account from the web app's state.
The web app must also redirect the user to the Microsoft identity platform logout endpoint to sign out.
When your web app redirects the user to the logout endpoint, this endpoint clears the user's session from the browser. If your app didn't go to the logout endpoint, the user will reauthenticate to your app without entering their credentials again. The reason is that they'll have a valid single sign-in session with the Microsoft identity platform.
During the application registration, you register a front-channel logout URL. In our tutorial, you registered https://localhost:44321/signout-oidc in the Front-channel logout URL field on the Authentication page. For details, see Register the webApp app.
During the application registration, you don't need to register an extra front-channel logout URL. The app will be called back on its main URL.
No front-channel logout URL is required in the application registration.
No front-channel logout URL is required in the application registration.
During the application registration, you don't need to register an extra front-channel logout URL. The app will be called back on its main URL.
In ASP.NET MVC, the sign-out button is exposed in Views\Shared\_LoginPartial.cshtml. It's displayed only when there's an authenticated account. That is, it's displayed when the user has previously signed in.
In previous versions of the ASP.NET Core templates, the Account controller was embedded with the web app. That's no longer the case because the controller is now part of the Microsoft.Identity.Web.UI NuGet package. See AccountController.cs for details.
Sets an OpenID redirect URI to /Account/SignedOut so that the controller is called back when Microsoft Entra ID has completed the sign-out.
Calls Signout(), which lets the OpenID Connect middleware contact the Microsoft identity platform logout endpoint. The endpoint then:
Clears the session cookie from the browser.
Calls back the post-logout redirect URI. By default, the post-logout redirect URI displays the signed-out view page SignedOut.cshtml.cs. This page is also provided as part of Microsoft.Identity.Web.
In ASP.NET, signing out is triggered from the SignOut() method on a controller (for instance, AccountController.cs#L25-L31). This method isn't part of the .NET Framework, contrary to what happens in ASP.NET Core. It:
Sends an OpenID sign-out challenge.
Clears the cache.
Redirects to the page that it wants.
/// <summary>
/// Send an OpenID Connect sign-out request.
/// </summary>
public void SignOut()
{
HttpContext.GetOwinContext()
.Authentication
.SignOut(CookieAuthenticationDefaults.AuthenticationType);
Response.Redirect("/");
}
In Java, sign-out is handled by calling the Microsoft identity platform logout endpoint directly and providing the post_logout_redirect_uri value. For details, see AuthPageController.java#L50-L60.
When the user selects the Sign out button, the app triggers the /auth/signout route, which destroys the session and redirects the browser to Microsoft identity platform sign-out endpoint.
logout(options = {}) {
return (req, res, next) => {
/**
* Construct a logout URI and redirect the user to end the
* session with Azure AD. For more information, visit:
* https://docs.microsoft.com/azure/active-directory/develop/v2-protocols-oidc#send-a-sign-out-request
*/
let logoutUri = `${this.msalConfig.auth.authority}/oauth2/v2.0/`;
if (options.postLogoutRedirectUri) {
logoutUri += `logout?post_logout_redirect_uri=${options.postLogoutRedirectUri}`;
}
req.session.destroy(() => {
res.redirect(logoutUri);
});
}
}
When the user selects Logout, the app triggers the logout route, which redirects the browser to the Microsoft identity platform sign-out endpoint.
The ASP.NET Core OpenID Connect middleware enables your app to intercept the call to the Microsoft identity platform logout endpoint by providing an OpenID Connect event named OnRedirectToIdentityProviderForSignOut. This is handled automatically by Microsoft.Identity.Web (which clears accounts in the case where your web app calls web apis)
In ASP.NET, you delegate to the middleware to execute the sign-out, clearing the session cookie:
public class AccountController : Controller
{
...
public void EndSession()
{
Request.GetOwinContext().Authentication.SignOut();
Request.GetOwinContext().Authentication.SignOut(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ApplicationCookie);
this.HttpContext.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
}
}
In the Java quickstart, the post-logout redirect URI just displays the index.html page.
In the Node quickstart, the post-logout redirect URI is used to redirect the browser back to sample home page after the user completes the logout process with the Microsoft identity platform.
In the Python quickstart, the post-logout redirect URI just displays the index.html page.
Protocol
If you want to learn more about sign-out, read the protocol documentation that's available from OpenID Connect.
Next steps
Learn more by building an ASP.NET Core web app that signs in users in the following multi-part tutorial series