⬆️ ⬇️

Authorization for lazy. Our rake



Hello! Recently we solved the task of authorizing users of a mobile application on our backend. So what, you ask, the problem has already been solved a thousand times. In this article, I will not tell a success story. Better to tell about the rake that we collected.



A little bit about the project



We at 2GIS make a cool and accurate directory of companies. To ensure the quality and relevance of data in 2GIS there are several internal systems. One of them is called YouLa - no, not the one where the ads are published. Our YouLa supports the process of reconciling data on the ground.



Part of the system is a mobile application with which pedestrians walk. Pedestrians - experts who bypass the entire city. The map is divided into different areas for verification.



See what the territorial division of the Moscow region looks like. Different colors on the map indicate different destination areas.





Pedestrians bring us data about organizations that cannot be called or do not have a website. For example, barbecue, kiosks with vegetables. In addition, it happens that the organization has changed the phone and we can not get through there. In all the cases mentioned, our specialist comes to the organization and checks the information on the ground.



Just a few days ago, we released a new mobile application for which we wrote a backend for data synchronization.



On our new backend we want to know what kind of user came to us.



We have several requirements for authorization:



- reliability and safety

- authentication by different sources,

- authentication of several types of Web, Mobile, API clients.



Select authentication method



To implement authentication, there are many different approaches, each has its own pros and cons. Given that we have a large number of integration points,

we decided not to reinvent the wheel and take an authentication and authorization provider based on OpenId Connect. For authorization on the backend we use JWT.



Read more in the article " Authentication and authorization in microservice applications ."



What is good about JWT and the OpenId Connect standard in Enterprise?



Now even within the framework of one company, systems are developed on different stacks of technologies and it is often difficult to make friends with them. As part of a single stack of technologies, you can also catch a lot of strange and unexpected effects , what can we say about the situation when you have several systems. For JWT and OpenId Connect, the list of supported clients and platforms is impressive .



The scheme of all components is as follows:



The protocol supports dynamic connection of authentication providers. We looked at two sources - Google+ and ADFS. But in the future we would like to quickly and easily expand the audience of the product, for example, by connecting other companies to the system who could solve their problems in our system.



With JWT, you can easily organize authentication of disparate clients. Moreover, many cloud clients offer at once a whole set of libraries that facilitate the integration of the provider into your application.



Cloud solutions



The first platform we decided to try was Auth0. The platform is very cool for the developer and for the administrator. It has detailed documentation, a beautiful and clear Web UI to configure all the parameters. In our Java / Kotlin application and backend authentication was bolted in a couple of hours.



The main advantages that we noted when working with the platform Auth0:



- detailed documentation and an infinite number of code examples in common programming languages;

- the ability to use for authentication not the web, but the native login form.



In order to implement support for JWT authentication in the backend, just write a few lines (this code for different platforms will differ only in the Authority and Audience parameters), in some cases you will need to specify the certificates to verify the signature of tokens:



Backend code on .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(); } 


In order to fasten authentication to the mobile phone - a few more lines:



Mobile ID code
  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(); } }); } }); } 


As you can see from the example, after authentication, two tokens come to us + another one (RefreshToken) is not shown in the code.



What are they needed for?



IdToken - contains user credentials

AccessToken - for authorization on API

RefreshToken - to update AccessToken



Tricky question: why do we need two Access and Refresh tokens?



Answer

Consider two cases of theft of keys:



  1. Scoundrel stole only AccessToken. Then it will be valid only until you use your RefreshToken.
  2. Scoundrel stole both tokens. Then, as soon as he takes advantage of RefreshToken, your tokens will no longer be valid and you will be logged out of the application. If you use your credentials, then the attacker's tokens will no longer work.

    Using two tokens limits the time for which an attacker will have access to your API.


The JWT-IdToken token itself looks like this:



From this token, the mobile application receives information about the authenticated user. Accordingly, we use IdToken to draw the user's full name and avatar.



AccessToken we attach to header requests:



API call from Android application
 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); } 


To authenticate the web client, it is also quite simple to perform an interactive login through the IdenitityProvider. Below is an example from the official documentation on how to tie this to the Angular4 application.



Backend authentication in web application
 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.`); } }); } } 


As you can see from the examples, no one left offended - the implementation for customers is simple and understandable.



For complete happiness, we lacked user authentication through our local Active Directory.



To configure synchronization between Auth0 and the local Active Directory, Auth0 provides powershell script.





When we were already glad that everything worked fine, and went to the admins with the request to set up synchronization between our AD and Auth0, we were refused. The guys said that the maximum where they are ready to pour our data is Azure. Also, the decision was affected by the fact that we already had an Office 365 subscription and some of the uchetk was already flooded in Azure.



Okay, we said.



Azure Active Directory B2C



Microsoft has a service called Azure Active Directory B2C. With the help of admins, we managed to set up synchronization of our AD with the Azure AD instance and configure the input through our Active Directory Federation Services (ADFS).



Configure Azure B2C login policies



At the time of this writing, the service is in the preview version, so only the most primitive scenarios can be configured through the UI, such as logging in via Google+ or Facebook. Logging in via Active Directory is done by downloading xml files through the Identity Experience Framework. It took about eight hours to debug the login scenarios + another day to refactor the entrance of the mobile phone and screw the Microsoft authentication provider.



On the backend, it was only necessary to specify a new IdentityProvider and Audience.



In order to configure the login, you will need to download the repository and go through the setup procedure described in the article. Just a few hours you program in xml - and voila! Your client is authenticated through Azure servers.



See and be terrified
 <?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, :



— ,

— ,

— .



:)



')

Source: https://habr.com/ru/post/351860/



All Articles