認証されていないユーザーがホーム ページにアクセスすると、app.py 内の index ルートによってユーザーが login ルートにリダイレクトされます。
@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__)
@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
))
ASP.NET では、Web アプリの [サインイン] ボタンを選択すると、AccountController コントローラーの SignIn アクションがトリガーされます。 以前のバージョンの ASP.NET Core テンプレートでは、Account コントローラーは Web アプリに埋め込まれていました。 現在では、コントローラーは Microsoft.Identity.Web.UI NuGet パッケージの一部になったため、これは当てはまらなくなりました。 詳細については、AccountController.cs を参照してください。
このコントローラーでは、Azure AD B2C アプリケーションも処理されます。
ASP.NET では、サインインはコントローラー (例: AccountController.cs#L16-L23) の SignIn() メソッドからトリガーされます。 このメソッドは、(ASP.NET Core での動作とは対照的に) .NET Framework の一部ではありません。 リダイレクト URI を提案した後に OpenID サインイン チャレンジを送信します。
public void SignIn()
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
Java では、サインアウトは、Microsoft ID プラットフォームの logout エンドポイントを直接呼び出し、post_logout_redirect_uri 値を提供することによって処理されます。 詳細については、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
ユーザーが [サインイン] リンクを/auth/signin選択するとルートがトリガーされ、サインイン コントローラーが引き継ぎて、Microsoft ID プラットフォームでユーザーを認証します。
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.microsoftonline.com/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;
}
}
ユーザーが [サインイン] リンクを選択すると、Microsoft ID プラットフォームの承認エンドポイントに移動します。
@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"))
ユーザーがアプリにサインインした後、ユーザーがサインアウトできるようにすることが必要です。
サインアウト
Web アプリからのサインアウトに必要なのは、サインインしたアカウントに関する情報を Web アプリの状態から削除することだけではありません。
サインアウトするには、Web アプリによってユーザーが Microsoft ID プラットフォーム logout エンドポイントにリダイレクトされる必要もあります。
Web アプリによってユーザーが logout エンドポイントにリダイレクトされると、このエンドポイントでは、ユーザーのセッションがブラウザーから消去されます。 アプリが logout エンドポイントに移動しなかった場合、ユーザーは資格情報を再入力しなくてもアプリに再認証されます。 理由は、Microsoft ID プラットフォームとの有効なシングル サインイン セッションがあるからです。
ユーザーが [サインアウト] ボタンを選択すると、アプリによって/auth/signoutルートがトリガーされてセッションが破棄され、ブラウザーが Microsoft ID プラットフォーム サインアウト エンドポイントにリダイレクトされます。
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);
});
}
}
ユーザーが [ログアウト] を選択すると、アプリによって logout ルートがトリガーされ、ブラウザーが Microsoft ID プラットフォームのサインアウト エンドポイントにリダイレクトされます。
ASP.NET Core OpenID Connect ミドルウェアでは、OnRedirectToIdentityProviderForSignOut という名前の OpenID Connect イベントを提供することで、お客様のアプリで Microsoft ID プラットフォーム logout エンドポイントへの呼び出しをインターセプトすることができます。 これは、Microsoft.Identity.Web によって自動的に処理されます (Web アプリによって Web API が呼び出される場合、アカウントはクリアされます)。
ASP.NET では、ミドルウェアに委任してサインアウトを実行し、セッション 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);
}
}
Java のクイックスタートでは、ログアウト後のリダイレクト URI は index.html ページを表示するだけです。
Node クイック スタートでは、ユーザーが Microsoft ID プラットフォームでログアウト プロセスを完了した後、ログアウト後のリダイレクト URI を使用して、ブラウザーをサンプル ホーム ページにリダイレクトします。
Python のクイックスタートでは、ログアウト後のリダイレクト URI は index.html ページを表示するだけです。