怠け者の承認。 熊手







みなさんこんにちは! 最近、バックエンドでモバイルアプリケーションのユーザーを認証する問題を解決しています。 だから、あなたが尋ねる、タスクはすでに千回解決ています。 この記事では、成功の話をしません。 集めたレーキについて教えてください。







プロジェクトについて少し



2GISでは、企業のクールで正確なディレクトリを作成します。 2GISのデータの品質と関連性を確保するために、いくつかの内部システムがあります。 それらの1つはYouLaと呼ばれます-いいえ、広告が公開されるものではありません。 YouTubeのYouLaは、地上でデータを調整するプロセスをサポートしています。







システムの一部は、歩行者が歩くモバイルアプリです。 歩行者は街全体を回る専門家です。 マップは、確認のために異なるセクションに分割されています。







モスクワ地方の領土区分がどのように見えるかをご覧ください。 マップ上の異なる色は、異なる目的地を示しています。













歩行者は、電話をかけられない組織やウェブサイトがない組織に関する情報をもたらします。 たとえば、バーベキュー、野菜の屋台。 さらに、組織が電話を変更したため、そこを通過できません。 上記のすべてのケースで、当社のスペシャリストが組織に来て、現場で情報を確認します。







ほんの数日前に、データ同期のバックエンドを作成する新しいモバイルアプリケーションを立ち上げました。







新しいバックエンドでは、どのようなユーザーが来たのかを知りたいと思っています。







承認にはいくつかの要件があります。







-信頼性と安全性、

-さまざまなソースからの認証、

-いくつかのタイプのクライアントWeb、モバイル、APIの認証。







認証方法の選択



認証の実装にはさまざまなアプローチがあり、それぞれに長所と短所があります。 多数の統合ポイントがあるため、

車輪を再発明せず、OpenId Connectに基づいた認証および承認プロバイダーを採用することにしました。 バックエンドでの承認には、JWTを使用します。







詳細については、「 マイクロサービスアプリケーションでの認証と承認 」をご覧ください。







JWTとエンタープライズのOpenId Connect標準の利点は何ですか?



現在、1つの会社のフレームワーク内であっても、システムはさまざまなテクノロジースタックで開発されており、後で友人を作ることは難しい場合がよくあります。 1つのテクノロジースタック内で、多くの奇妙で予想外の効果をキャッチすることもでき、複数のシステムがある場合の状況は言うまでもありません。 JWTおよびOpenId Connectの場合、サポートされるクライアントとプラットフォームのリストは印象的です。







すべてのコンポーネントの操作スキームは次のようになります。







このプロトコルは、認証プロバイダーの動的接続をサポートしています。 Google+とADFSの2つのソースを調べました。 しかし、将来的には、たとえば、システムの問題を解決できるシステムに他の企業を接続することにより、製品の対象者を簡単かつ迅速に拡大したいと考えています。







JWTを使用すると、多彩なクライアントの認証を簡単に整理できます。 さらに、多くのクラウドクライアントは、アプリケーションへのプロバイダーの統合を促進するライブラリセット全体をすぐに提供します。







クラウドソリューション



私たちが試した最初のプラットフォームはAuth0です。 プラットフォームは、開発者と管理者の両方にとって非常にクールです。 詳細なドキュメント、すべてのパラメーターを設定するための美しく直感的なWeb UIがあります。 Java / Kotlinアプリケーションとバックエンドでは、認証は数時間で完了しました。







Auth0プラットフォームで作業するときに注意した主な利点:







-一般的なプログラミング言語の詳細なドキュメントと無限のコードサンプル。

-認証に使用する機能は、Webではなく、ネイティブのログインフォームです。







バックエンドでJWT認証のサポートを実装するには、ほんの数行を書くだけで十分です(異なるプラットフォームのこのコードはAuthorityおよびAudienceパラメーターのみが異なります)。場合によっては、トークン署名を検証する証明書も指定する必要があります。







.NET Coreバックエンドコード
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); // 1. Add Authentication Services services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.Authority = "https://devday2gis.auth0.com/"; options.Audience = "https://devday.api"; }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // 2. Add Authentication app.UseAuthentication(); app.UseMvc(); }
      
      





携帯電話への認証を固定するために-さらにいくつかの行:







携帯電話のログインコード
  private void login() { token.setText("Not logged in"); Auth0 auth0 = new Auth0(this); auth0.setOIDCConformant(true); WebAuthProvider.init(auth0) .withScheme("demo") .withAudience(String.format("https://%s/userinfo", getString(R.string.com_auth0_domain))) .withScope("openid email profile") .start(MainActivity.this, new AuthCallback() { @Override public void onSuccess(@NonNull final Credentials credentials) { runOnUiThread(new Runnable() { @Override public void run() { idToken = credentials.getIdToken(); accessToken = credentials.getAccessToken(); Log.d("id token", credentials.getIdToken()); Log.d("access token", credentials.getAccessToken()); token.setText("Logged in: " + credentials.getIdToken()); } }); } @Override public void onFailure(@NonNull final Dialog dialog) { runOnUiThread(new Runnable() { @Override public void run() { dialog.show(); } }); } @Override public void onFailure(final AuthenticationException exception) { runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, "Error: " + exception.getMessage(), Toast.LENGTH_SHORT).show(); } }); } }); }
      
      





この例からわかるように、認証後、2つのトークンが追加され、もう1つ(RefreshToken)はコードに表示されません。







彼らは何のために?



IdToken-ユーザー資格情報が含まれています

AccessToken-APIでの承認用

RefreshToken-AccessTokenの更新用







バックフィルの質問:なぜ2つのアクセストークンとリフレッシュトークンが必要なのですか?







答え

キー窃盗の2つのケースを検討してください。







  1. 悪党はAccessTokenのみを盗みました。 その後、RefreshTokenを使用するまで有効になります。
  2. 悪党は両方のトークンを盗みました。 その後、彼がRefreshTokenを使用するとすぐに、トークンが機能しなくなり、アプリケーションからログアウトします。 資格情報を使用すると、攻撃者のトークンは機能しなくなります。

    2つのトークンを使用すると、攻撃者がAPIにアクセスできる時間が制限されます。


JWT-IdTokenトークン自体は次のようになります。







モバイルアプリケーションは、このトークンから認証済みユーザーに関する情報を受け取ります。 したがって、IdTokenを使用して、ユーザーとそのアバターの名前を描画します。







リクエストのヘッダーに添付するAccessToken:







AndroidアプリケーションからのAPI呼び出し
 private void makeApiCall() { DevDayApi api = CreateApi("http://rnd-123.2gis.local/", accessToken); api.getUserProfile().enqueue(new Callback<ApiResponse>() { @Override public void onResponse(Call<ApiResponse> call, Response<ApiResponse> response) { runOnUiThread(() -> { if(response.body() != null) apiAnswer.setText(response.body().Answer); }); } @Override public void onFailure(Call<ApiResponse> call, Throwable t) { apiAnswer.setText(t.getMessage()); } }); } private DevDayApi CreateApi(String baseUrl, String authToken) { HttpLoggingInterceptor logging = new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.HEADERS); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(chain -> { Request newRequest = chain.request().newBuilder() .addHeader("Authorization", "Bearer " + authToken)//tokenProvider.getAuthToken()) .build(); return chain.proceed(newRequest); }) .addInterceptor(logging) .build(); Retrofit retrofit = new Retrofit.Builder() .client(client) .baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build(); return retrofit.create(DevDayApi.class); }
      
      





Webクライアント認証の場合、IdenitityProviderを介した対話型ログインも非常に簡単です。 以下は、これをAngular4アプリケーションに固定する方法に関する公式ドキュメントの例です。







Webアプリケーションバックエンド認証
 import { Injectable } from '@angular/core'; import { AUTH_CONFIG } from './auth0-variables'; import { Router } from '@angular/router'; import * as auth0 from 'auth0-js'; @Injectable() export class AuthService { auth0 = new auth0.WebAuth({ clientID: AUTH_CONFIG.clientID, domain: AUTH_CONFIG.domain, responseType: 'token id_token', audience: `https://${AUTH_CONFIG.domain}/userinfo`, redirectUri: AUTH_CONFIG.callbackURL, scope: 'openid' }); constructor(public router: Router) {} public login(): void { this.auth0.authorize(); } public handleAuthentication(): void { this.auth0.parseHash((err, authResult) => { if (authResult && authResult.accessToken && authResult.idToken) { this.setSession(authResult); this.router.navigate(['/home']); } else if (err) { this.router.navigate(['/home']); console.log(err); alert(`Error: ${err.error}. Check the console for further details.`); } }); } }
      
      





例からわかるように、誰も気分を害することはありませんでした-顧客への実装はシンプルで簡単です。







完全に幸福にするために、ローカルActive Directoryによるユーザー認証がありませんでした。







Auth0とローカルActive Directory間の同期を構成するために、Auth0にはPowerShellスクリプトが用意されています。













すべてが正常に機能し、ADとAuth0間の同期をセットアップするようにというリクエストで管理者に問い合わせてくれたことがすでに嬉しかったので、拒否されました。 彼らは、私たちのデータを注ぐ準備ができている最大の場所はAzureだと言いました。 また、この決定は、Office 365サブスクリプションを既に使用しており、一部のアカウントが既にAzureにアップロードされているという事実に影響されました。







さて、言った。







Azure Active Directory B2C



Microsoftには、Azure Active Directory B2Cというサービスがあります。 管理者の助けを借りて、ADとAzure ADインスタンスの同期を構成し、Active Directoryフェデレーションサービス(ADFS)を介してログインを構成できました。







Azure B2Cログインポリシーを構成する



執筆時点では、サービスはプレビュー版であるため、Google +やFacebook経由でのログインなど、UIを介して最も基本的なスクリプトのみを構成できます。 Active Directoryを介したログインは、Identity Experience Frameworkを介してxmlファイルをダウンロードすることにより行われます。 ログインスクリプトをデバッグするのに約8時間かかり、モバイルログインをリファクタリングしてマイクロソフトの認証プロバイダーを固定するのにもう1日かかりました。







バックエンドでは、新しいIdentityProviderとAudienceを示すことだけが必要でした。







エントランスを設定するには、 リポジトリをダウンロードし、記事に記載されているセットアップ手順を実行する必要があります 。 ほんの数時間でxmlでプログラミングします-そして出来上がりです! クライアントはAzureサーバーを介して認証します。







見て怖がる
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <TrustFrameworkPolicy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" PolicySchemaVersion="0.3.0.0" TenantId="yourtenant.onmicrosoft.com" PolicyId="B2C_1A_TrustFrameworkBase" PublicPolicyUri="http://yourtenant.onmicrosoft.com/B2C_1A_TrustFrameworkBase"> <BuildingBlocks> <ClaimsSchema> <!-- The ClaimsSchema is divided into three sections: 1. Section I lists the minimum claims that are required for the user journeys to work properly. 2. Section II lists the claims required for query string parameters and other special parameters to be passed to other claims providers, esp. login.microsoftonline.com for authentication. Please do not modify these claims. 3. Section III lists any additional (optional) claims that can be collected from the user, stored in the directory and sent in tokens during sign in. Add new claims to be collected from the user and/or sent in the token in Section III. --> <!-- NOTE: The claims schema contains restrictions on certain claims such as passwords and usernames. The trust framework policy treats Azure AD as any other claims provider and all its restrictions are modelled in the policy. A policy could be modified to add more restrictions, or use another claims provider for credential storage which will have its own restrictions. --> <!-- SECTION I: Claims required for user journeys to work properly --> <ClaimType Id="socialIdpUserId"> <DisplayName>Username</DisplayName> <DataType>string</DataType> <UserHelpText/> <UserInputType>TextBox</UserInputType> <Restriction> <Pattern RegularExpression="^[a-zA-Z0-9]+[a-zA-Z0-9_-]*$" HelpText="The username you provided is not valid. It must begin with an alphabet or number and can contain alphabets, numbers and the following symbols: _ -" /> </Restriction> </ClaimType> <ClaimType Id="tenantId"> <DisplayName>User's Object's Tenant ID</DisplayName> <DataType>string</DataType> <DefaultPartnerClaimTypes> <Protocol Name="OAuth2" PartnerClaimType="tid" /> <Protocol Name="OpenIdConnect" PartnerClaimType="tid" /> <Protocol Name="SAML2" PartnerClaimType="http://schemas.microsoft.com/identity/claims/tenantid" /> </DefaultPartnerClaimTypes> <UserHelpText>Tenant identifier (ID) of the user object in Azure AD.</UserHelpText> </ClaimType> <ClaimType Id="objectId"> <DisplayName>User's Object ID</DisplayName> <DataType>string</DataType> <DefaultPartnerClaimTypes> <Protocol Name="OAuth2" PartnerClaimType="oid" /> <Protocol Name="OpenIdConnect" PartnerClaimType="oid" /> <Protocol Name="SAML2" PartnerClaimType="http://schemas.microsoft.com/identity/claims/objectidentifier" /> </DefaultPartnerClaimTypes> <UserHelpText>Object identifier (ID) of the user object in Azure AD.</UserHelpText> </ClaimType> <!-- Claims needed for local accounts. --> <ClaimType Id="signInName"> <DisplayName>Sign in name</DisplayName> <DataType>string</DataType> <UserHelpText/> <UserInputType>TextBox</UserInputType> </ClaimType> <ClaimType Id="signInNames.emailAddress"> <DisplayName>Email Address</DisplayName> <DataType>string</DataType> <UserHelpText>Email address to use for signing in.</UserHelpText> <UserInputType>TextBox</UserInputType> </ClaimType> <ClaimType Id="password"> <DisplayName>Password</DisplayName> <DataType>string</DataType> <UserHelpText>Enter password</UserHelpText> <UserInputType>Password</UserInputType> </ClaimType> <!-- The claim types newPassword and reenterPassword are considered special, please do not change the names. The UI validates the the user correctly re-entered their password during account creation based on these claim types. --> <ClaimType Id="newPassword"> <DisplayName>New Password</DisplayName> <DataType>string</DataType> <UserHelpText>Enter new password</UserHelpText> <UserInputType>Password</UserInputType> <Restriction> <Pattern RegularExpression="^((?=.*[az])(?=.*[AZ])(?=.*\d)|(?=.*[az])(?=.*[AZ])(?=.*[^A-Za-z0-9])|(?=.*[az])(?=.*\d)(?=.*[^A-Za-z0-9])|(?=.*[AZ])(?=.*\d)(?=.*[^A-Za-z0-9]))([A-Za-z\d@#$%^&amp;*\-_+=[\]{}|\\:',?/`~&quot;();!]|\.(?!@)){8,16}$" HelpText="8-16 characters, containing 3 out of 4 of the following: Lowercase characters, uppercase characters, digits (0-9), and one or more of the following symbols: @ # $ % ^ &amp; * - _ + = [ ] { } | \ : ' , ? / ` ~ &quot; ( ) ; ." /> </Restriction> </ClaimType> <!-- The password regular expression above is constructed for AAD passwords based on restrictions at https://msdn.microsoft.com/en-us/library/azure/jj943764.aspx ^( # one of the following four combinations must appear in the password (?=.*[az])(?=.*[AZ])(?=.*\d) | # matches lower case, upper case or digit (?=.*[az])(?=.*[AZ])(?=.*[^A-Za-z0-9]) | # matches lower case, upper case or special character (ie non-alpha or digit) (?=.*[az])(?=.*\d)(?=.*[^A-Za-z0-9]) | # matches lower case, digit, or special character (?=.*[AZ])(?=.*\d)(?=.*[^A-Za-z0-9]) # matches upper case, digit, or special character ) ( # The password must match the following restrictions [A-Za-z\d@#$%^&*\-_+=[\]{}|\\:',?/`~"();!] | # The list of all acceptable characters (without .) \.(?!@) # or . can appear as long as not followed by @ ) {8,16}$ # the length must be between 8 and 16 chars inclusive --> <ClaimType Id="reenterPassword"> <DisplayName>Confirm New Password</DisplayName> <DataType>string</DataType> <UserHelpText>Confirm new password</UserHelpText> <UserInputType>Password</UserInputType> <Restriction> <Pattern RegularExpression="^((?=.*[az])(?=.*[AZ])(?=.*\d)|(?=.*[az])(?=.*[AZ])(?=.*[^A-Za-z0-9])|(?=.*[az])(?=.*\d)(?=.*[^A-Za-z0-9])|(?=.*[AZ])(?=.*\d)(?=.*[^A-Za-z0-9]))([A-Za-z\d@#$%^&amp;*\-_+=[\]{}|\\:',?/`~&quot;();!]|\.(?!@)){8,16}$" HelpText=" " /> </Restriction> </ClaimType> <ClaimType Id="passwordPolicies"> <DisplayName>Password Policies</DisplayName> <DataType>string</DataType> <UserHelpText>Password policies used by Azure AD to determine password strength, expiry etc.</UserHelpText> </ClaimType> <ClaimType Id="client_id"> <DisplayName>client_id</DisplayName> <DataType>string</DataType> <AdminHelpText>Special parameter passed to EvoSTS.</AdminHelpText> <UserHelpText>Special parameter passed to EvoSTS.</UserHelpText> </ClaimType> <ClaimType Id="resource_id"> <DisplayName>resource_id</DisplayName> <DataType>string</DataType> <AdminHelpText>Special parameter passed to EvoSTS.</AdminHelpText> <UserHelpText>Special parameter passed to EvoSTS.</UserHelpText> </ClaimType> <ClaimType Id="sub"> <DisplayName>Subject</DisplayName> <DataType>string</DataType> <DefaultPartnerClaimTypes> <Protocol Name="OpenIdConnect" PartnerClaimType="sub" /> </DefaultPartnerClaimTypes> <UserHelpText/> </ClaimType> <ClaimType Id="alternativeSecurityId"> <DisplayName>AlternativeSecurityId</DisplayName> <DataType>string</DataType> <UserHelpText/> </ClaimType> <ClaimType Id="mailNickName"> <DisplayName>MailNickName</DisplayName> <DataType>string</DataType> <UserHelpText>Your mail nick name as stored in the Azure Active Directory.</UserHelpText> </ClaimType> <ClaimType Id="identityProvider"> <DisplayName>Identity Provider</DisplayName> <DataType>string</DataType> <DefaultPartnerClaimTypes> <Protocol Name="OAuth2" PartnerClaimType="idp" /> <Protocol Name="OpenIdConnect" PartnerClaimType="idp" /> <Protocol Name="SAML2" PartnerClaimType="http://schemas.microsoft.com/identity/claims/identityprovider" /> </DefaultPartnerClaimTypes> <UserHelpText/> </ClaimType> <ClaimType Id="displayName"> <DisplayName>Display Name</DisplayName> <DataType>string</DataType> <DefaultPartnerClaimTypes> <Protocol Name="OAuth2" PartnerClaimType="unique_name" /> <Protocol Name="OpenIdConnect" PartnerClaimType="name" /> <Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" /> </DefaultPartnerClaimTypes> <UserHelpText>Your display name.</UserHelpText> <UserInputType>TextBox</UserInputType> </ClaimType> <ClaimType Id="strongAuthenticationPhoneNumber"> <DisplayName>Phone Number</DisplayName> <DataType>string</DataType> <Mask Type="Simple">XXX-XXX-</Mask> <UserHelpText>Your telephone number</UserHelpText> </ClaimType> <ClaimType Id="Verified.strongAuthenticationPhoneNumber"> <DisplayName>Verified Phone Number</DisplayName> <DataType>string</DataType> <DefaultPartnerClaimTypes> <Protocol Name="OpenIdConnect" PartnerClaimType="phone_number" /> </DefaultPartnerClaimTypes> <Mask Type="Simple">XXX-XXX-</Mask> <UserHelpText>Your office phone number that has been verified</UserHelpText> </ClaimType> <ClaimType Id="newPhoneNumberEntered"> <DisplayName>New Phone Number Entered</DisplayName> <DataType>boolean</DataType> </ClaimType> <ClaimType Id="userIdForMFA"> <DisplayName>UserId for MFA</DisplayName> <DataType>string</DataType> </ClaimType> <ClaimType Id="email"> <DisplayName>Email Address</DisplayName> <DataType>string</DataType> <DefaultPartnerClaimTypes> <Protocol Name="OpenIdConnect" PartnerClaimType="email" /> </DefaultPartnerClaimTypes> <UserHelpText>Email address that can be used to contact you.</UserHelpText> <UserInputType>TextBox</UserInputType> <Restriction> <Pattern RegularExpression="^[a-zA-Z0-9.!#$%&amp;'^_`{}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$" HelpText="Please enter a valid email address." /> </Restriction> </ClaimType> <ClaimType Id="otherMails"> <DisplayName>Alternate Email Addresses</DisplayName> <DataType>stringCollection</DataType> <UserHelpText>Email addresses that can be used to contact the user.</UserHelpText> </ClaimType> <ClaimType Id="userPrincipalName"> <DisplayName>UserPrincipalName</DisplayName> <DataType>string</DataType> <DefaultPartnerClaimTypes> <Protocol Name="OAuth2" PartnerClaimType="upn" /> <Protocol Name="OpenIdConnect" PartnerClaimType="upn" /> <Protocol Name="SAML2" PartnerClaimType="http://schemas.microsoft.com/identity/claims/userprincipalname" /> </DefaultPartnerClaimTypes> <UserHelpText>Your user name as stored in the Azure Active Directory.</UserHelpText> </ClaimType> <ClaimType Id="upnUserName"> <DisplayName>UPN User Name</DisplayName> <DataType>string</DataType> <UserHelpText>The user name for creating user principal name.</UserHelpText> </ClaimType> <ClaimType Id="newUser"> <DisplayName>User is new</DisplayName> <DataType>boolean</DataType> <UserHelpText/> </ClaimType> <ClaimType Id="executed-SelfAsserted-Input"> <DisplayName>Executed-SelfAsserted-Input</DisplayName> <DataType>string</DataType> <UserHelpText>A claim that specifies whether attributes were collected from the user.</UserHelpText> </ClaimType> <ClaimType Id="authenticationSource"> <DisplayName>AuthenticationSource</DisplayName> <DataType>string</DataType> <UserHelpText>Specifies whether the user was authenticated at Social IDP or local account.</UserHelpText> </ClaimType> <!-- SECTION II: Claims required to pass on special parameters (including some query string parameters) to other claims providers --> <ClaimType Id="nca"> <DisplayName>nca</DisplayName> <DataType>string</DataType> <UserHelpText>Special parameter passed for local account authentication to login.microsoftonline.com.</UserHelpText> </ClaimType> <ClaimType Id="grant_type"> <DisplayName>grant_type</DisplayName> <DataType>string</DataType> <UserHelpText>Special parameter passed for local account authentication to login.microsoftonline.com.</UserHelpText> </ClaimType> <ClaimType Id="scope"> <DisplayName>scope</DisplayName> <DataType>string</DataType> <UserHelpText>Special parameter passed for local account authentication to login.microsoftonline.com.</UserHelpText> </ClaimType> <ClaimType Id="objectIdFromSession"> <DisplayName>objectIdFromSession</DisplayName> <DataType>boolean</DataType> <UserHelpText>Parameter provided by the default session management provider to indicate that the object id has been retrieved from an SSO session.</UserHelpText> </ClaimType> <ClaimType Id="isActiveMFASession"> <DisplayName>isActiveMFASession</DisplayName> <DataType>boolean</DataType> <UserHelpText>Parameter provided by the MFA session management to indicate that the user has an active MFA session.</UserHelpText> </ClaimType> <!-- SECTION III: Additional claims that can be collected from the users, stored in the directory, and sent in the token. Add additional claims here. --> <ClaimType Id="givenName"> <DisplayName>Given Name</DisplayName> <DataType>string</DataType> <DefaultPartnerClaimTypes> <Protocol Name="OAuth2" PartnerClaimType="given_name" /> <Protocol Name="OpenIdConnect" PartnerClaimType="given_name" /> <Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" /> </DefaultPartnerClaimTypes> <UserHelpText>Your given name (also known as first name).</UserHelpText> <UserInputType>TextBox</UserInputType> </ClaimType> <ClaimType Id="surname"> <DisplayName>Surname</DisplayName> <DataType>string</DataType> <DefaultPartnerClaimTypes> <Protocol Name="OAuth2" PartnerClaimType="family_name" /> <Protocol Name="OpenIdConnect" PartnerClaimType="family_name" /> <Protocol Name="SAML2" PartnerClaimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname" /> </DefaultPartnerClaimTypes> <UserHelpText>Your surname (also known as family name or last name).</UserHelpText> <UserInputType>TextBox</UserInputType> </ClaimType> </ClaimsSchema> <ClaimsTransformations> <ClaimsTransformation Id="CreateOtherMailsFromEmail" TransformationMethod="AddItemToStringCollection"> <InputClaims> <InputClaim ClaimTypeReferenceId="email" TransformationClaimType="item" /> <InputClaim ClaimTypeReferenceId="otherMails" TransformationClaimType="collection" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="otherMails" TransformationClaimType="collection" /> </OutputClaims> </ClaimsTransformation> <ClaimsTransformation Id="CreateRandomUPNUserName" TransformationMethod="CreateRandomString"> <InputParameters> <InputParameter Id="randomGeneratorType" DataType="string" Value="GUID" /> </InputParameters> <OutputClaims> <OutputClaim ClaimTypeReferenceId="upnUserName" TransformationClaimType="outputClaim" /> </OutputClaims> </ClaimsTransformation> <ClaimsTransformation Id="CreateUserPrincipalName" TransformationMethod="FormatStringClaim"> <InputClaims> <InputClaim ClaimTypeReferenceId="upnUserName" TransformationClaimType="inputClaim" /> </InputClaims> <InputParameters> <InputParameter Id="stringFormat" DataType="string" Value="cpim_{0}@{RelyingPartyTenantId}" /> </InputParameters> <OutputClaims> <OutputClaim ClaimTypeReferenceId="userPrincipalName" TransformationClaimType="outputClaim" /> </OutputClaims> </ClaimsTransformation> <ClaimsTransformation Id="CreateAlternativeSecurityId" TransformationMethod="CreateAlternativeSecurityId"> <InputClaims> <InputClaim ClaimTypeReferenceId="socialIdpUserId" TransformationClaimType="key" /> <InputClaim ClaimTypeReferenceId="identityProvider" TransformationClaimType="identityProvider" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="alternativeSecurityId" TransformationClaimType="alternativeSecurityId" /> </OutputClaims> </ClaimsTransformation> <ClaimsTransformation Id="CreateUserIdForMFA" TransformationMethod="FormatStringClaim"> <InputClaims> <InputClaim ClaimTypeReferenceId="objectId" TransformationClaimType="inputClaim" /> </InputClaims> <InputParameters> <InputParameter Id="stringFormat" DataType="string" Value="{0}@{RelyingPartyTenantId}" /> </InputParameters> <OutputClaims> <OutputClaim ClaimTypeReferenceId="userIdForMFA" TransformationClaimType="outputClaim" /> </OutputClaims> </ClaimsTransformation> <ClaimsTransformation Id="CreateSubjectClaimFromAlternativeSecurityId" TransformationMethod="CreateStringClaim"> <InputParameters> <InputParameter Id="value" DataType="string" Value="Not supported currently. Use oid claim." /> </InputParameters> <OutputClaims> <OutputClaim ClaimTypeReferenceId="sub" TransformationClaimType="createdClaim" /> </OutputClaims> </ClaimsTransformation> </ClaimsTransformations> <ClientDefinitions> <ClientDefinition Id="DefaultWeb"> <ClientUIFilterFlags>LineMarkers, MetaRefresh</ClientUIFilterFlags> </ClientDefinition> </ClientDefinitions> <ContentDefinitions> <!-- This content definition is to render an error page that displays unhandled errors. --> <ContentDefinition Id="api.error"> <LoadUri>~/tenant/default/exception.cshtml</LoadUri> <RecoveryUri>~/common/default_page_error.html</RecoveryUri> <DataUri>urn:com:microsoft:aad:b2c:elements:globalexception:1.1.0</DataUri> <Metadata> <Item Key="DisplayName">Error page</Item> </Metadata> </ContentDefinition> <ContentDefinition Id="api.idpselections"> <LoadUri>~/tenant/default/idpSelector.cshtml</LoadUri> <RecoveryUri>~/common/default_page_error.html</RecoveryUri> <DataUri>urn:com:microsoft:aad:b2c:elements:idpselection:1.0.0</DataUri> <Metadata> <Item Key="DisplayName">Idp selection page</Item> <Item Key="language.intro">Sign in</Item> </Metadata> </ContentDefinition> <ContentDefinition Id="api.idpselections.signup"> <LoadUri>~/tenant/default/idpSelector.cshtml</LoadUri> <RecoveryUri>~/common/default_page_error.html</RecoveryUri> <DataUri>urn:com:microsoft:aad:b2c:elements:idpselection:1.0.0</DataUri> <Metadata> <Item Key="DisplayName">Idp selection page</Item> <Item Key="language.intro">Sign up</Item> </Metadata> </ContentDefinition> <ContentDefinition Id="api.signuporsignin"> <LoadUri>~/tenant/default/unified.cshtml</LoadUri> <RecoveryUri>~/common/default_page_error.html</RecoveryUri> <DataUri>urn:com:microsoft:aad:b2c:elements:unifiedssp:1.0.0</DataUri> <Metadata> <Item Key="DisplayName">Signin and Signup</Item> </Metadata> </ContentDefinition> <ContentDefinition Id="api.phonefactor"> <LoadUri>~/tenant/default/multifactor-1.0.0.cshtml</LoadUri> <RecoveryUri>~/common/default_page_error.html</RecoveryUri> <DataUri>urn:com:microsoft:aad:b2c:elements:multifactor:1.1.0</DataUri> <Metadata> <Item Key="DisplayName">Multi-factor authentication page</Item> </Metadata> </ContentDefinition> <ContentDefinition Id="api.selfasserted"> <LoadUri>~/tenant/default/selfAsserted.cshtml</LoadUri> <RecoveryUri>~/common/default_page_error.html</RecoveryUri> <DataUri>urn:com:microsoft:aad:b2c:elements:selfasserted:1.1.0</DataUri> <Metadata> <Item Key="DisplayName">Collect information from user page</Item> </Metadata> </ContentDefinition> <ContentDefinition Id="api.selfasserted.profileupdate"> <LoadUri>~/tenant/default/updateProfile.cshtml</LoadUri> <RecoveryUri>~/common/default_page_error.html</RecoveryUri> <DataUri>urn:com:microsoft:aad:b2c:elements:selfasserted:1.1.0</DataUri> <Metadata> <Item Key="DisplayName">Collect information from user page</Item> </Metadata> </ContentDefinition> <ContentDefinition Id="api.localaccountsignup"> <LoadUri>~/tenant/default/selfAsserted.cshtml</LoadUri> <RecoveryUri>~/common/default_page_error.html</RecoveryUri> <DataUri>urn:com:microsoft:aad:b2c:elements:selfasserted:1.1.0</DataUri> <Metadata> <Item Key="DisplayName">Local account sign up page</Item> </Metadata> </ContentDefinition> <ContentDefinition Id="api.localaccountpasswordreset"> <LoadUri>~/tenant/default/selfAsserted.cshtml</LoadUri> <RecoveryUri>~/common/default_page_error.html</RecoveryUri> <DataUri>urn:com:microsoft:aad:b2c:elements:selfasserted:1.1.0</DataUri> <Metadata> <Item Key="DisplayName">Local account change password page</Item> </Metadata> </ContentDefinition> </ContentDefinitions> </BuildingBlocks> <!-- A list of all the claim providers that can be used in the technical policies. If a claims provider is not listed in this section, then it cannot be used in a technical policy. --> <ClaimsProviders> <ClaimsProvider> <!-- The following Domain element allows this profile to be used if the request comes with domain_hint query string parameter, eg domain_hint=facebook.com --> <Domain>facebook.com</Domain> <DisplayName>Facebook</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="Facebook-OAUTH"> <!-- The text in the following DisplayName element is shown to the user on the claims provider selection screen. --> <DisplayName>Facebook</DisplayName> <Protocol Name="OAuth2" /> <Metadata> <Item Key="ProviderName">facebook</Item> <Item Key="authorization_endpoint">https://www.facebook.com/dialog/oauth</Item> <Item Key="AccessTokenEndpoint">https://graph.facebook.com/oauth/access_token</Item> <Item Key="HttpBinding">GET</Item> <Item Key="UsePolicyInRedirectUri">0</Item> <!-- The Facebook required HTTP GET method, but the access token response is in JSON format from 3/27/2017 --> <Item Key="AccessTokenResponseFormat">json</Item> </Metadata> <CryptographicKeys> <Key Id="client_secret" StorageReferenceId="B2C_1A_FacebookSecret" /> </CryptographicKeys> <InputClaims /> <OutputClaims> <OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="id" /> <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="first_name" /> <OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="last_name" /> <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" /> <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" /> <OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="facebook.com" /> <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" /> </OutputClaims> <OutputClaimsTransformations> <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" /> <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" /> <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" /> </OutputClaimsTransformations> <UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" /> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>Local Account SignIn</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="login-NonInteractive"> <DisplayName>Local Account SignIn</DisplayName> <Protocol Name="OpenIdConnect" /> <Metadata> <Item Key="UserMessageIfClaimsPrincipalDoesNotExist">We can't seem to find your account</Item> <Item Key="UserMessageIfInvalidPassword">Your password is incorrect</Item> <Item Key="UserMessageIfOldPasswordUsed">Looks like you used an old password</Item> <Item Key="ProviderName">https://sts.windows.net/</Item> <Item Key="METADATA">https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration</Item> <Item Key="authorization_endpoint">https://login.microsoftonline.com/{tenant}/oauth2/token</Item> <Item Key="response_types">id_token</Item> <Item Key="response_mode">query</Item> <Item Key="scope">email openid</Item> <!-- Policy Engine Clients --> <Item Key="UsePolicyInRedirectUri">false</Item> <Item Key="HttpBinding">POST</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username" Required="true" /> <InputClaim ClaimTypeReferenceId="password" Required="true" /> <InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="password" /> <InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid" /> <InputClaim ClaimTypeReferenceId="nca" PartnerClaimType="nca" DefaultValue="1" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="oid" /> <OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid" /> <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" /> <OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" /> <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" /> <OutputClaim ClaimTypeReferenceId="userPrincipalName" PartnerClaimType="upn" /> <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" /> </OutputClaims> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>PhoneFactor</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="PhoneFactor-InputOrVerify"> <DisplayName>PhoneFactor</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="ContentDefinitionReferenceId">api.phonefactor</Item> <Item Key="ManualPhoneNumberEntryAllowed">true</Item> </Metadata> <CryptographicKeys> <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /> </CryptographicKeys> <InputClaimsTransformations> <InputClaimsTransformation ReferenceId="CreateUserIdForMFA" /> </InputClaimsTransformations> <InputClaims> <InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" /> <InputClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" /> <OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" /> </OutputClaims> <UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA" /> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>Azure Active Directory</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="AAD-Common"> <DisplayName>Azure Active Directory</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.AzureActiveDirectoryProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <CryptographicKeys> <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /> </CryptographicKeys> <!-- We need this here to suppress the SelfAsserted provider from invoking SSO on validation profiles. --> <IncludeInSso>false</IncludeInSso> <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" /> </TechnicalProfile> <!-- Technical profiles for social logins --> <TechnicalProfile Id="AAD-UserWriteUsingAlternativeSecurityId"> <Metadata> <Item Key="Operation">Write</Item> <Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item> <Item Key="UserMessageIfClaimsPrincipalAlreadyExists">You are already registered, please press the back button and sign in instead.</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaimsTransformations> <InputClaimsTransformation ReferenceId="CreateOtherMailsFromEmail" /> </InputClaimsTransformations> <InputClaims> <InputClaim ClaimTypeReferenceId="AlternativeSecurityId" PartnerClaimType="alternativeSecurityId" Required="true" /> </InputClaims> <PersistedClaims> <!-- Required claims --> <PersistedClaim ClaimTypeReferenceId="alternativeSecurityId" /> <PersistedClaim ClaimTypeReferenceId="userPrincipalName" /> <PersistedClaim ClaimTypeReferenceId="mailNickName" DefaultValue="unknown" /> <PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="unknown" /> <!-- Optional claims --> <PersistedClaim ClaimTypeReferenceId="otherMails" /> <PersistedClaim ClaimTypeReferenceId="givenName" /> <PersistedClaim ClaimTypeReferenceId="surname" /> </PersistedClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" /> <!-- The following other mails claim is needed for the case when a user is created, we get otherMails from directory. Self-asserted provider also has an OutputClaims, and if this is absent, Self-Asserted provider will prompt the user for otherMails. --> <OutputClaim ClaimTypeReferenceId="otherMails" /> </OutputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" /> </TechnicalProfile> <TechnicalProfile Id="AAD-UserReadUsingAlternativeSecurityId"> <Metadata> <Item Key="Operation">Read</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item> <Item Key="UserMessageIfClaimsPrincipalDoesNotExist">User does not exist. Please sign up before you can sign in.</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="AlternativeSecurityId" PartnerClaimType="alternativeSecurityId" Required="true" /> </InputClaims> <OutputClaims> <!-- Required claims --> <OutputClaim ClaimTypeReferenceId="objectId" /> <!-- Optional claims --> <OutputClaim ClaimTypeReferenceId="userPrincipalName" /> <OutputClaim ClaimTypeReferenceId="displayName" /> <OutputClaim ClaimTypeReferenceId="otherMails" /> <OutputClaim ClaimTypeReferenceId="givenName" /> <OutputClaim ClaimTypeReferenceId="surname" /> </OutputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile> <TechnicalProfile Id="AAD-UserReadUsingAlternativeSecurityId-NoError"> <Metadata> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item> </Metadata> <IncludeTechnicalProfile ReferenceId="AAD-UserReadUsingAlternativeSecurityId" /> </TechnicalProfile> <!-- Technical profiles for local accounts --> <TechnicalProfile Id="AAD-UserWriteUsingLogonEmail"> <Metadata> <Item Key="Operation">Write</Item> <Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaims> <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" Required="true" /> </InputClaims> <PersistedClaims> <!-- Required claims --> <PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" /> <PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password"/> <PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="unknown" /> <PersistedClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration" /> <PersistedClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="strongAuthenticationPhoneNumber" /> <!-- Optional claims. --> <PersistedClaim ClaimTypeReferenceId="givenName" /> <PersistedClaim ClaimTypeReferenceId="surname" /> </PersistedClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" /> <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" /> <OutputClaim ClaimTypeReferenceId="userPrincipalName" /> <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" /> </OutputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" /> </TechnicalProfile> <TechnicalProfile Id="AAD-UserReadUsingEmailAddress"> <Metadata> <Item Key="Operation">Read</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item> <Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaims> <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames" Required="true" /> </InputClaims> <OutputClaims> <!-- Required claims --> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" /> <OutputClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" /> <!-- Optional claims --> <OutputClaim ClaimTypeReferenceId="userPrincipalName" /> <OutputClaim ClaimTypeReferenceId="displayName" /> <OutputClaim ClaimTypeReferenceId="otherMails" /> <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" /> </OutputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile> <TechnicalProfile Id="AAD-UserWritePasswordUsingObjectId"> <Metadata> <Item Key="Operation">Write</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaims> <InputClaim ClaimTypeReferenceId="objectId" Required="true" /> </InputClaims> <PersistedClaims> <PersistedClaim ClaimTypeReferenceId="objectId" /> <PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password"/> <!-- If the user stepped up during password reset, their phone number should be persisted for future authentication requests. --> <PersistedClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="strongAuthenticationPhoneNumber" /> </PersistedClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile> <!-- Technical profiles for updating user record using objectId --> <TechnicalProfile Id="AAD-UserWriteProfileUsingObjectId"> <Metadata> <Item Key="Operation">Write</Item> <Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaims> <InputClaim ClaimTypeReferenceId="objectId" Required="true" /> </InputClaims> <PersistedClaims> <!-- Required claims --> <PersistedClaim ClaimTypeReferenceId="objectId" /> <!-- If the user stepped up during password reset, their phone number should be persisted for future authentication requests. --> <PersistedClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="strongAuthenticationPhoneNumber" /> <!-- Optional claims --> <PersistedClaim ClaimTypeReferenceId="givenName" /> <PersistedClaim ClaimTypeReferenceId="surname" /> </PersistedClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile> <!-- The following technical profile is used to read data after user authenticates. --> <TechnicalProfile Id="AAD-UserReadUsingObjectId"> <Metadata> <Item Key="Operation">Read</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaims> <InputClaim ClaimTypeReferenceId="objectId" Required="true" /> </InputClaims> <OutputClaims> <!-- Required claims --> <OutputClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" /> <!-- Optional claims --> <OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" /> <OutputClaim ClaimTypeReferenceId="displayName" /> <OutputClaim ClaimTypeReferenceId="otherMails" /> <OutputClaim ClaimTypeReferenceId="givenName" /> <OutputClaim ClaimTypeReferenceId="surname" /> </OutputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile> <TechnicalProfile Id="AAD-UserWritePhoneNumberUsingObjectId"> <Metadata> <Item Key="Operation">Write</Item> <Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaims> <InputClaim ClaimTypeReferenceId="objectId" Required="true" /> </InputClaims> <PersistedClaims> <PersistedClaim ClaimTypeReferenceId="objectId" /> <PersistedClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="strongAuthenticationPhoneNumber" /> </PersistedClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>Self Asserted</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="SelfAsserted-Social"> <DisplayName>User ID signup</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="ContentDefinitionReferenceId">api.selfasserted</Item> </Metadata> <CryptographicKeys> <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /> </CryptographicKeys> <InputClaims> <!-- These claims ensure that any values retrieved in the previous steps (eg from an external IDP) are prefilled. Note that some of these claims may not have any value, for example, if the external IDP did not provide any of these values, or if the claim did not appear in the OutputClaims section of the IDP. In addition, if a claim is not in the InputClaims section, but it is in the OutputClaims section, then its value will not be prefilled, but the user will still be prompted for it (with an empty value). --> <InputClaim ClaimTypeReferenceId="displayName" /> <InputClaim ClaimTypeReferenceId="givenName" /> <InputClaim ClaimTypeReferenceId="surname" /> </InputClaims> <OutputClaims> <!-- These claims are not shown to the user because their value is obtained through the "ValidationTechnicalProfiles" referenced below, or a default value is assigned to the claim. A claim is only shown to the user to provide a value if its value cannot be obtained through any other means. --> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="newUser" /> <OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" /> <!-- Optional claims. These claims are collected from the user and can be modified. If a claim is to be persisted in the directory after having been collected from the user, it needs to be added as a PersistedClaim in the ValidationTechnicalProfile referenced below, ie in AAD-UserWriteUsingAlternativeSecurityId. --> <OutputClaim ClaimTypeReferenceId="displayName" /> <OutputClaim ClaimTypeReferenceId="givenName" /> <OutputClaim ClaimTypeReferenceId="surname" /> </OutputClaims> <ValidationTechnicalProfiles> <ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingAlternativeSecurityId" /> </ValidationTechnicalProfiles> <UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialSignup" /> </TechnicalProfile> <TechnicalProfile Id="SelfAsserted-ProfileUpdate"> <DisplayName>User ID signup</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="ContentDefinitionReferenceId">api.selfasserted.profileupdate</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaims> <InputClaim ClaimTypeReferenceId="alternativeSecurityId" /> <InputClaim ClaimTypeReferenceId="userPrincipalName" /> <!-- Optional claims. These claims are collected from the user and can be modified. Any claim added here should be updated in the ValidationTechnicalProfile referenced below so it can be written to directory after being updateed by the user, ie AAD-UserWriteProfileUsingObjectId. --> <InputClaim ClaimTypeReferenceId="givenName" /> <InputClaim ClaimTypeReferenceId="surname" /> </InputClaims> <OutputClaims> <!-- Required claims --> <OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" /> <!-- Optional claims. These claims are collected from the user and can be modified. Any claim added here should be updated in the ValidationTechnicalProfile referenced below so it can be written to directory after being updateed by the user, ie AAD-UserWriteProfileUsingObjectId. --> <OutputClaim ClaimTypeReferenceId="givenName" /> <OutputClaim ClaimTypeReferenceId="surname" /> </OutputClaims> <ValidationTechnicalProfiles> <ValidationTechnicalProfile ReferenceId="AAD-UserWriteProfileUsingObjectId" /> </ValidationTechnicalProfiles> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>Local Account</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="LocalAccountSignUpWithLogonEmail"> <DisplayName>Email signup</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="IpAddressClaimReferenceId">IpAddress</Item> <Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item> <Item Key="language.button_continue">Create</Item> </Metadata> <CryptographicKeys> <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /> </CryptographicKeys> <InputClaims> <InputClaim ClaimTypeReferenceId="email" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" /> <OutputClaim ClaimTypeReferenceId="newPassword" Required="true" /> <OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" /> <OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" /> <OutputClaim ClaimTypeReferenceId="authenticationSource" /> <OutputClaim ClaimTypeReferenceId="newUser" /> <!-- Optional claims, to be collected from the user --> <OutputClaim ClaimTypeReferenceId="displayName" /> <OutputClaim ClaimTypeReferenceId="givenName" /> <OutputClaim ClaimTypeReferenceId="surName" /> </OutputClaims> <ValidationTechnicalProfiles> <ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" /> </ValidationTechnicalProfiles> <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" /> </TechnicalProfile> <!-- This technical profile uses a validation technical profile to authenticate the user. --> <TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email"> <DisplayName>Local Account Signin</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="SignUpTarget">SignUpWithLogonEmailExchange</Item> <Item Key="setting.operatingMode">Email</Item> <Item Key="ContentDefinitionReferenceId">api.selfasserted</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaims> <InputClaim ClaimTypeReferenceId="signInName" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="signInName" Required="true" /> <OutputClaim ClaimTypeReferenceId="password" Required="true" /> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="authenticationSource" /> </OutputClaims> <ValidationTechnicalProfiles> <ValidationTechnicalProfile ReferenceId="login-NonInteractive" /> </ValidationTechnicalProfiles> <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" /> </TechnicalProfile> <!-- This technical profile forces the user to verify the email address that they provide on the UI. Only after email is verified, the user account is read from the directory. --> <TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress"> <DisplayName>Reset password using email address</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="IpAddressClaimReferenceId">IpAddress</Item> <Item Key="ContentDefinitionReferenceId">api.localaccountpasswordreset</Item> </Metadata> <CryptographicKeys> <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /> </CryptographicKeys> <IncludeInSso>false</IncludeInSso> <OutputClaims> <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" /> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="userPrincipalName" /> <OutputClaim ClaimTypeReferenceId="authenticationSource" /> <OutputClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" /> </OutputClaims> <ValidationTechnicalProfiles> <ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" /> </ValidationTechnicalProfiles> </TechnicalProfile> <TechnicalProfile Id="LocalAccountWritePasswordUsingObjectId"> <DisplayName>Change password (username)</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="ContentDefinitionReferenceId">api.localaccountpasswordreset</Item> </Metadata> <CryptographicKeys> <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /> </CryptographicKeys> <InputClaims> <InputClaim ClaimTypeReferenceId="objectId" /> <InputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="newPassword" Required="true" /> <OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" /> </OutputClaims> <ValidationTechnicalProfiles> <ValidationTechnicalProfile ReferenceId="AAD-UserWritePasswordUsingObjectId" /> </ValidationTechnicalProfiles> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>Session Management</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="SM-Noop"> <DisplayName>Noop Session Management Provider</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.NoopSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </TechnicalProfile> <TechnicalProfile Id="SM-AAD"> <DisplayName>Session Mananagement Provider</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.DefaultSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <PersistedClaims> <PersistedClaim ClaimTypeReferenceId="objectId" /> <PersistedClaim ClaimTypeReferenceId="signInName" /> <PersistedClaim ClaimTypeReferenceId="authenticationSource" /> <PersistedClaim ClaimTypeReferenceId="identityProvider" /> <PersistedClaim ClaimTypeReferenceId="newUser" /> <PersistedClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" /> </PersistedClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectIdFromSession" DefaultValue="true"/> </OutputClaims> </TechnicalProfile> <!-- Profile name is being used to disambiguate AAD session between sign up and sign in --> <TechnicalProfile Id="SM-SocialSignup"> <IncludeTechnicalProfile ReferenceId="SM-AAD" /> </TechnicalProfile> <TechnicalProfile Id="SM-SocialLogin"> <DisplayName>Session Mananagement Provider</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.ExternalLoginSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <Metadata> <Item Key="AlwaysFetchClaimsFromProvider">true</Item> </Metadata> <PersistedClaims> <PersistedClaim ClaimTypeReferenceId="AlternativeSecurityId" /> </PersistedClaims> </TechnicalProfile> <TechnicalProfile Id="SM-MFA"> <DisplayName>Session Mananagement Provider</DisplayName> <Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.DefaultSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> <PersistedClaims> <PersistedClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" /> </PersistedClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="isActiveMFASession" DefaultValue="true"/> </OutputClaims> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>Trustframework Policy Engine TechnicalProfiles</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13"> <DisplayName>Trustframework Policy Engine Default Technical Profile</DisplayName> <Protocol Name="None" /> <Metadata> <Item Key="url">{service:te}</Item> </Metadata> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> <ClaimsProvider> <DisplayName>Token Issuer</DisplayName> <TechnicalProfiles> <TechnicalProfile Id="JwtIssuer"> <DisplayName>JWT Issuer</DisplayName> <Protocol Name="None" /> <OutputTokenFormat>JWT</OutputTokenFormat> <Metadata> <Item Key="client_id">{service:te}</Item> <Item Key="issuer_refresh_token_user_identity_claim_type">objectId</Item> <Item Key="SendTokenResponseBodyWithJsonNumbers">true</Item> </Metadata> <CryptographicKeys> <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" /> <Key Id="issuer_refresh_token_key" StorageReferenceId="B2C_1A_TokenEncryptionKeyContainer" /> </CryptographicKeys> <InputClaims /> <OutputClaims /> </TechnicalProfile> </TechnicalProfiles> </ClaimsProvider> </ClaimsProviders> <UserJourneys> <UserJourney Id="SignUpOrSignIn"> <OrchestrationSteps> <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin"> <ClaimsProviderSelections> <ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange" /> <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" /> </ClaimsProviderSelections> <ClaimsExchanges> <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" /> </ClaimsExchanges> </OrchestrationStep> <!-- Check if the user has selected to sign in using one of the social providers --> <OrchestrationStep Order="2" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimsExist" ExecuteActionsIf="true"> <Value>objectId</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH" /> <ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" /> </ClaimsExchanges> </OrchestrationStep> <!-- For social IDP authentication, attempt to find the user account in the directory. --> <OrchestrationStep Order="3" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimEquals" ExecuteActionsIf="true"> <Value>authenticationSource</Value> <Value>localAccountAuthentication</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" /> </ClaimsExchanges> </OrchestrationStep> <!-- Show self-asserted page only if the directory does not have the user account already (ie we do not have an objectId). This can only happen when authentication happened using a social IDP. If local account was created or authentication done using ESTS in step 2, then an user account must exist in the directory by this time. --> <OrchestrationStep Order="4" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimsExist" ExecuteActionsIf="true"> <Value>objectId</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social" /> </ClaimsExchanges> </OrchestrationStep> <!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent in the token. --> <OrchestrationStep Order="5" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimEquals" ExecuteActionsIf="true"> <Value>authenticationSource</Value> <Value>socialIdpAuthentication</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" /> </ClaimsExchanges> </OrchestrationStep> <!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect from the user. So, in that case, create the user in the directory if one does not already exist (verified using objectId which would be set from the last step if account was created in the directory. --> <OrchestrationStep Order="6" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimsExist" ExecuteActionsIf="true"> <Value>objectId</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" /> </ClaimsExchanges> </OrchestrationStep> <!-- Phone verification: If MFA is not required, the next three steps (#5-#7) should be removed. This step checks whether there's a phone number on record, for the user. If found, then the user is challenged to verify it. --> <OrchestrationStep Order="7" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimsExist" ExecuteActionsIf="true"> <Value>isActiveMFASession</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="PhoneFactor-Verify" TechnicalProfileReferenceId="PhoneFactor-InputOrVerify" /> </ClaimsExchanges> </OrchestrationStep> <!-- Save MFA phone number: The precondition verifies whether the user provided a new number in the previous step. If so, then the phone number is stored in the directory for future authentication requests. --> <OrchestrationStep Order="8" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimsExist" ExecuteActionsIf="false"> <Value>newPhoneNumberEntered</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="AADUserWriteWithObjectId" TechnicalProfileReferenceId="AAD-UserWritePhoneNumberUsingObjectId" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="9" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" /> </OrchestrationSteps> <ClientDefinition ReferenceId="DefaultWeb" /> </UserJourney> <UserJourney Id="ProfileEdit"> <OrchestrationSteps> <OrchestrationStep Order="1" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.idpselections"> <ClaimsProviderSelections> <ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange" /> <ClaimsProviderSelection TargetClaimsExchangeId="LocalAccountSigninEmailExchange" /> </ClaimsProviderSelections> </OrchestrationStep> <OrchestrationStep Order="2" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH" /> <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="3" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimEquals" ExecuteActionsIf="true"> <Value>authenticationSource</Value> <Value>localAccountAuthentication</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="AADUserRead" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="4" Type="ClaimsExchange"> <Preconditions> <Precondition Type="ClaimEquals" ExecuteActionsIf="true"> <Value>authenticationSource</Value> <Value>socialIdpAuthentication</Value> <Action>SkipThisOrchestrationStep</Action> </Precondition> </Preconditions> <ClaimsExchanges> <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" /> </ClaimsExchanges> </OrchestrationStep> <!-- If the user ever stepped up to use 2FA, profile update must verify this because the user will be able to change their sign in email address or strong authentication email here. This guards against scenarios where a user's password is stolen, the attacker can change the email addresses leaving no way for the user to recover their account. By requiring 2FA, stolen passwords cannot be used to take over the account completely. --> <OrchestrationStep Order="5" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="PhoneFactor" TechnicalProfileReferenceId="PhoneFactor-InputOrVerify" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="6" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="B2CUserProfileUpdateExchange" TechnicalProfileReferenceId="SelfAsserted-ProfileUpdate" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="7" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" /> </OrchestrationSteps> <ClientDefinition ReferenceId="DefaultWeb" /> </UserJourney> <UserJourney Id="PasswordReset"> <OrchestrationSteps> <OrchestrationStep Order="1" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="2" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="PhoneFactor-Verify" TechnicalProfileReferenceId="PhoneFactor-InputOrVerify" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="3" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" /> </ClaimsExchanges> </OrchestrationStep> <OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" /> </OrchestrationSteps> <ClientDefinition ReferenceId="DefaultWeb" /> </UserJourney> </UserJourneys> </TrustFrameworkPolicy>
      
      





. , , ( , ).







Azure Active Directory B2C ADFS :









Active Directory
 public class AuthImpl { private static final String TAG = AuthImpl.class.getSimpleName(); private Activity activity; private PublicClientApplication authClient; private AuthTokenProvider authTokenProvider; private String[] scopes; @Inject AuthImpl(AppCompatActivity activity, PublicClientApplication authClient, AuthTokenProvider authTokenProvider) { this.activity = activity; this.authClient = authClient; this.authTokenProvider = authTokenProvider; this.scopes = Constants.SCOPES.split("\\s+"); } public boolean isUserAuthenticated() { return AndroidHelpers.isEmulator() || getCurrentUser() != null; } private User getCurrentUser() { try { return Helpers.getUserByPolicy(authClient.getUsers(), Constants.SISU_POLICY); } catch (MsalClientException e) { /* No token in cache, proceed with normal unauthenticated app experience */ Log.d(TAG, "MSAL Exception Generated while getting users: " + e.toString()); } catch (IndexOutOfBoundsException e) { Log.d(TAG, "User at this position does not exist: " + e.toString()); } return null; } public Single<AuthResultDto> login() { /* Attempt to get a user and acquireTokenSilently * If this fails we will do an interactive request */ return Single.create(emitter -> { try { User currentUser = Helpers.getUserByPolicy(authClient.getUsers(), Constants.SISU_POLICY); if (currentUser != null) { /* We have 1 user */ authClient.acquireTokenSilentAsync( scopes, currentUser, String.format(Constants.AUTHORITY, Constants.TENANT, Constants.SISU_POLICY), false, getAuthSilentCallback(emitter)); } else { /* We have no user */ authClient.acquireToken(activity, scopes, getAuthInteractiveCallback(emitter)); } } catch (MsalClientException e) { /* No token in cache, proceed with normal unauthenticated app experience */ Log.d(TAG, "MSAL Exception Generated while getting users: " + e.toString()); } catch (IndexOutOfBoundsException e) { Log.d(TAG, "User at this position does not exist: " + e.toString()); } }); } // // App callbacks for MSAL // ====================== // getActivity() - returns activity so we can acquireToken within a callback // getAuthSilentCallback() - callback defined to handle acquireTokenSilent() case // getAuthInteractiveCallback() - callback defined to handle acquireToken() case // /* Callback used in for silent acquireToken calls. * Looks if tokens are in the cache (refreshes if necessary and if we don't forceRefresh) * else errors that we need to do an interactive request. */ private AuthenticationCallback getAuthSilentCallback(final SingleEmitter<AuthResultDto> emitter) { return new AuthenticationCallback() { @Override public void onSuccess(AuthenticationResult authenticationResult) { /* Successfully got a token, call api now */ Log.d(TAG, "Successfully authenticated"); emitter.onSuccess(new AuthResultDto(authenticationResult.getIdToken())); } @Override public void onError(MsalException exception) { /* Failed to acquireToken */ Log.d(TAG, "Authentication failed: " + exception.toString()); if (exception instanceof MsalClientException) { /* Exception inside MSAL, more info inside MsalError.java */ emitter.onError(exception); assert true; } else if (exception instanceof MsalServiceException) { /* Exception when communicating with the STS, likely config issue */ emitter.onError(exception); assert true; } else if (exception instanceof MsalUiRequiredException) { /* Tokens expired or no session, retry with interactive */ authClient.acquireToken(activity, scopes, getAuthInteractiveCallback(emitter)); } } @Override public void onCancel() { /* User canceled the authentication */ Log.d(TAG, "User cancelled login."); emitter.onSuccess(new AuthResultDto(true)); } }; } /* Callback used for interactive request. If succeeds we use the access * token to call the api. Does not check cache. */ private AuthenticationCallback getAuthInteractiveCallback(final SingleEmitter<AuthResultDto> emitter) { return new AuthenticationCallback() { @Override public void onSuccess(AuthenticationResult authenticationResult) { /* Successfully got a token, call api now */ Log.d(TAG, "Successfully authenticated"); Log.d(TAG, "ID Token: " + authenticationResult.getIdToken()); authTokenProvider.setAuthToken(authenticationResult.getIdToken()); emitter.onSuccess(new AuthResultDto(authenticationResult.getIdToken())); } @Override public void onError(MsalException exception) { /* Failed to acquireToken */ Log.d(TAG, "Authentication failed: " + exception.toString()); if (exception instanceof MsalClientException) { /* Exception inside MSAL, more info inside MsalError.java */ emitter.onError(exception); assert true; } else if (exception instanceof MsalServiceException) { /* Exception when communicating with the STS, likely config issue */ emitter.onError(exception); assert true; } } @Override public void onCancel() { /* User canceled the authentication */ Log.d(TAG, "User cancelled login."); emitter.onSuccess(new AuthResultDto(true)); } }; } static final int BROWSER_FLOW = 1001; boolean handleInteractiveRequestRedirect(int requestCode, int resultCode, final Intent data){ if(requestCode == BROWSER_FLOW) { authClient.handleInteractiveRequestRedirect(requestCode, resultCode, data); return true; } return false; } }
      
      





. , :







  1. , Google Chrome.
  2. Azure AD .
  3. ADFS . Chrome .


Google Firebase Auth



, , , . Google Firebase Auth, .







, Google Firebase Auth .







Google, Firebase .







Firebase
  private final FirebaseAuth authInstance; private CompositeDisposable disposables; private EventSubscriber eventSubscriber; public Single<AuthResultDto> login(boolean forceRefreshToken) { if (disposables != null) { disposables.dispose(); } disposables = new CompositeDisposable(); return Single.create(emitter -> { FirebaseUser user = authInstance.getCurrentUser(); if (user == null || !user.isEmailVerified()) { List<AuthUI.IdpConfig> providers = Arrays.asList( new AuthUI.IdpConfig.EmailBuilder().setRequireName(true).build(), new AuthUI.IdpConfig.GoogleBuilder().build()); activity.startActivityForResult( AuthUI.getInstance() .createSignInIntentBuilder() .setAvailableProviders(providers) .build(), MainActivity.REQUES_LOGIN); } else { if (forceRefreshToken) { refreshToken(); } else { emitter.onSuccess(new AuthResultDto(false)); } } disposables.add(this.eventSubscriber.getEvent(new AuthResultDto(true)) .subscribe(e -> { emitter.onSuccess(e); disposables.dispose(); })); }); } private void refreshToken() { FirebaseUser user = authInstance.getCurrentUser(); user.getIdToken(true) .addOnCompleteListener(task -> { if (task.isSuccessful()) { GetTokenResult tokenResult = task.getResult(); String idToken = tokenResult.getToken(); //  idToken eventSubscriber.Publish(new AuthResultDto( false)); // ... } else { AuthResultDto result = new AuthResultDto( false); result.Error = new Exception(task.getException()); eventSubscriber.Publish(new AuthResultDto(false)); } }); }
      
      





, Firebase, Azure AD . Azure.









, , .







Auth0 — , . API, . - , :)







Azure Active Directory B2C — Enterprise. , . , xml. — B2C , . , .







Firebase Auth — . , . — - .







?







, OpenId Connect, :







— ,

— ,

— .







:)








All Articles