Skip to content

Commit 844c0bd

Browse files
authored
Native auth: JIT feature implementation (#2293)
This is the last PR that contains all the changes related to the JIT feature (registration of a new strong authentication method). MSAL Common PR: [#2639](AzureAD/microsoft-authentication-library-common-for-android#2639)
1 parent fda5cac commit 844c0bd

File tree

12 files changed

+700
-7
lines changed

12 files changed

+700
-7
lines changed

changelog

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ MSAL Wiki : https://github.com/AzureAD/microsoft-authentication-library-for-andr
22

33
vNext
44
----------
5+
- [MINOR] Native auth: Add claimsRequest also to getAccessToken and signIn after signUp/SSPR flows (#2278)
6+
- [MINOR] Native auth: user can now register new strong authentication method when required (#2293)
57

68
Version 6.0.1
79
----------
@@ -11,7 +13,6 @@ Version 6.0.0
1113
----------
1214
- [PATCH] Update common @21.0.0
1315
- [MAJOR] Update minSDK to 21
14-
- [MINOR] Native auth: Add claimsRequest also to getAccessToken and signIn after signUp/SSPR flows (#2278)
1516

1617
Version 5.10.2
1718
----------

common

Submodule common updated 58 files

msal/src/main/java/com/microsoft/identity/client/internal/CommandParametersAdapter.java

Lines changed: 100 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,10 @@
3232
import com.microsoft.identity.client.IAccount;
3333
import com.microsoft.identity.client.ITenantProfile;
3434
import com.microsoft.identity.client.MultiTenantAccount;
35-
import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleApi;
36-
import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleCredential;
37-
import com.microsoft.identity.common.internal.msafederation.google.SignInWithGoogleParameters;
3835
import com.microsoft.identity.common.internal.platform.AndroidPlatformUtil;
3936
import com.microsoft.identity.common.java.logging.DiagnosticContext;
37+
import com.microsoft.identity.common.java.nativeauth.commands.parameters.JITChallengeAuthMethodCommandParameters;
38+
import com.microsoft.identity.common.java.nativeauth.commands.parameters.JITContinueCommandParameters;
4039
import com.microsoft.identity.nativeauth.AuthMethod;
4140
import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplicationConfiguration;
4241
import com.microsoft.identity.client.PoPAuthenticationScheme;
@@ -1125,6 +1124,104 @@ public static ResetPasswordSubmitNewPasswordCommandParameters createResetPasswor
11251124
return commandParameters;
11261125
}
11271126

1127+
/**
1128+
* Creates command parameter for [[com.microsoft.identity.common.nativeauth.internal.commands.JITChallengeAuthMethodCommand]] of Native Auth
1129+
* @param configuration PCA configuration
1130+
* @param tokenCache token cache for storing results
1131+
* @param verificationContact the email/phone to send the challenge to
1132+
* @param challengeChannel the channel used to send the challenge
1133+
* @param challengeType the type of challenge
1134+
* @param correlationId correlation ID to use in the API request, taken from the previous request in the flow
1135+
* @param continuationToken Continuation token
1136+
* @return Command parameter object
1137+
* @throws ClientException
1138+
*/
1139+
public static JITChallengeAuthMethodCommandParameters createJITChallengeAuthMethodCommandParameters(
1140+
@NonNull final NativeAuthPublicClientApplicationConfiguration configuration,
1141+
@NonNull final OAuth2TokenCache tokenCache,
1142+
@NonNull final String verificationContact,
1143+
@NonNull final String challengeChannel,
1144+
@NonNull final String challengeType,
1145+
@NonNull final String correlationId,
1146+
@NonNull final String continuationToken
1147+
) throws ClientException {
1148+
final NativeAuthCIAMAuthority authority = ((NativeAuthCIAMAuthority) configuration.getDefaultAuthority());
1149+
1150+
final AbstractAuthenticationScheme authenticationScheme = AuthenticationSchemeFactory.createScheme(
1151+
AndroidPlatformComponentsFactory.createFromContext(configuration.getAppContext()),
1152+
null
1153+
);
1154+
1155+
return JITChallengeAuthMethodCommandParameters.builder()
1156+
.authenticationScheme(authenticationScheme)
1157+
.platformComponents(AndroidPlatformComponentsFactory.createFromContext(configuration.getAppContext()))
1158+
.applicationName(configuration.getAppContext().getPackageName())
1159+
.applicationVersion(getPackageVersion(configuration.getAppContext()))
1160+
.clientId(configuration.getClientId())
1161+
.isSharedDevice(configuration.getIsSharedDevice())
1162+
.redirectUri(configuration.getRedirectUri())
1163+
.oAuth2TokenCache(tokenCache)
1164+
.requiredBrokerProtocolVersion(configuration.getRequiredBrokerProtocolVersion())
1165+
.sdkType(SdkType.MSAL)
1166+
.sdkVersion(PublicClientApplication.getSdkVersion())
1167+
.powerOptCheckEnabled(configuration.isPowerOptCheckForEnabled())
1168+
.authority(authority)
1169+
.verificationContact(verificationContact)
1170+
.authMethodChallengeType(challengeType)
1171+
.continuationToken(continuationToken)
1172+
.correlationId(correlationId)
1173+
.challengeChannel(challengeChannel)
1174+
.build();
1175+
}
1176+
1177+
/**
1178+
* Creates command parameter for [[com.microsoft.identity.common.nativeauth.internal.commands.JITContinueCommandParameters]] of Native Auth
1179+
* @param configuration PCA configuration
1180+
* @param tokenCache token cache for storing results
1181+
* @param grantType grant type
1182+
* @param code the code provided by the user
1183+
* @param correlationId correlation ID to use in the API request, taken from the previous request in the flow
1184+
* @param continuationToken continuation token
1185+
* @return Command parameter object
1186+
* @throws ClientException
1187+
*/
1188+
public static JITContinueCommandParameters createJITSubmitChallengeCommandParameters(
1189+
@NonNull final NativeAuthPublicClientApplicationConfiguration configuration,
1190+
@NonNull final OAuth2TokenCache tokenCache,
1191+
@NonNull final String grantType,
1192+
@NonNull final String code,
1193+
@NonNull final String correlationId,
1194+
@NonNull final String continuationToken
1195+
) throws ClientException {
1196+
final NativeAuthCIAMAuthority authority = ((NativeAuthCIAMAuthority) configuration.getDefaultAuthority());
1197+
1198+
final AbstractAuthenticationScheme authenticationScheme = AuthenticationSchemeFactory.createScheme(
1199+
AndroidPlatformComponentsFactory.createFromContext(configuration.getAppContext()),
1200+
null
1201+
);
1202+
1203+
return JITContinueCommandParameters.builder()
1204+
.authenticationScheme(authenticationScheme)
1205+
.platformComponents(AndroidPlatformComponentsFactory.createFromContext(configuration.getAppContext()))
1206+
.applicationName(configuration.getAppContext().getPackageName())
1207+
.applicationVersion(getPackageVersion(configuration.getAppContext()))
1208+
.clientId(configuration.getClientId())
1209+
.isSharedDevice(configuration.getIsSharedDevice())
1210+
.redirectUri(configuration.getRedirectUri())
1211+
.oAuth2TokenCache(tokenCache)
1212+
.requiredBrokerProtocolVersion(configuration.getRequiredBrokerProtocolVersion())
1213+
.sdkType(SdkType.MSAL)
1214+
.sdkVersion(PublicClientApplication.getSdkVersion())
1215+
.powerOptCheckEnabled(configuration.isPowerOptCheckForEnabled())
1216+
.authority(authority)
1217+
.grantType(grantType)
1218+
.continuationToken(continuationToken)
1219+
.code(code)
1220+
.correlationId(correlationId)
1221+
.build();
1222+
}
1223+
1224+
11281225
private static String getPackageVersion(@NonNull final Context context) {
11291226
final String packageName = context.getPackageName();
11301227
try {

msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplication.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ import com.microsoft.identity.nativeauth.statemachine.results.SignUpResult
7878
import com.microsoft.identity.nativeauth.statemachine.states.AccountState
7979
import com.microsoft.identity.nativeauth.statemachine.states.AwaitingMFAState
8080
import com.microsoft.identity.nativeauth.statemachine.states.Callback
81+
import com.microsoft.identity.nativeauth.statemachine.states.RegisterStrongAuthState
8182
import com.microsoft.identity.nativeauth.statemachine.states.ResetPasswordCodeRequiredState
8283
import com.microsoft.identity.nativeauth.statemachine.states.SignInCodeRequiredState
8384
import com.microsoft.identity.nativeauth.statemachine.states.SignInContinuationState
@@ -771,6 +772,17 @@ class NativeAuthPublicClientApplication(
771772
)
772773
}
773774

775+
is SignInCommandResult.StrongAuthMethodRegistrationRequired -> {
776+
SignInResult.StrongAuthMethodRegistrationRequired(
777+
nextState = RegisterStrongAuthState(
778+
continuationToken = result.continuationToken,
779+
correlationId = result.correlationId,
780+
config = nativeAuthConfig
781+
),
782+
authMethods = result.authMethods.toListOfAuthMethods()
783+
)
784+
}
785+
774786
is INativeAuthCommandResult.Redirect -> {
775787
SignInError(
776788
errorType = ErrorTypes.BROWSER_REQUIRED,
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.microsoft.identity.nativeauth.parameters
2+
3+
import com.microsoft.identity.nativeauth.AuthMethod
4+
5+
/**
6+
* Encapsulates the parameters passed to the challengeAuthMethod methods of RegisterStrongAuthState
7+
*/
8+
class NativeAuthChallengeAuthMethodParameters(
9+
/**
10+
* authentication method to challenge
11+
*/
12+
val authMethod: AuthMethod
13+
) {
14+
15+
/**
16+
* email to contact to register a new strong authentication method
17+
*/
18+
var verificationContact: String? = null
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.microsoft.identity.nativeauth.parameters
2+
3+
import com.microsoft.identity.nativeauth.statemachine.states.RegisterStrongAuthVerificationRequiredState
4+
5+
class NativeAuthRegisterStrongAuthVerificationRequiredResultParameter internal constructor(
6+
internal val nextState: RegisterStrongAuthVerificationRequiredState,
7+
internal val codeLength: Int,
8+
internal val sentTo: String,
9+
internal val channel: String
10+
) {
11+
12+
/**
13+
* The next state to use to continue the strong authentication method registration flow.
14+
*/
15+
fun getNextState(): RegisterStrongAuthVerificationRequiredState {
16+
return nextState
17+
}
18+
19+
/**
20+
* The length of the challenge required by the server.
21+
*/
22+
fun getCodeLength(): Int {
23+
return codeLength
24+
}
25+
26+
/**
27+
* The email/phone number the challenge was sent to.
28+
*/
29+
fun getSentTo(): String {
30+
return sentTo
31+
}
32+
33+
/**
34+
* the channel(email/phone) the challenge was sent through.
35+
*/
36+
fun getChannel(): String {
37+
return channel
38+
}
39+
}

msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/errors/Error.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ internal class ErrorTypes {
8383
*/
8484
const val INVALID_USERNAME = "invalid_username"
8585

86+
/*
87+
* The INVALID_INPUT value indicates the input provided by the user is incorrect.
88+
* The input needs be re-submitted.
89+
*/
90+
const val INVALID_INPUT = "invalid_input"
91+
8692
/*
8793
* The INVALID_STATE value indicates a misconfigured or expired state, or an internal error
8894
* in state transitions. If this occurs, the flow should be restarted.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.microsoft.identity.nativeauth.statemachine.errors
2+
3+
import com.microsoft.identity.nativeauth.statemachine.results.RegisterStrongAuthChallengeResult
4+
import com.microsoft.identity.nativeauth.statemachine.results.RegisterStrongAuthSubmitChallengeResult
5+
6+
class RegisterStrongAuthChallengeError(
7+
override val errorType: String? = null,
8+
override val error: String? = null,
9+
override val errorMessage: String?,
10+
override val correlationId: String,
11+
override val errorCodes: List<Int>? = null,
12+
override var exception: Exception? = null
13+
): RegisterStrongAuthChallengeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
14+
{
15+
fun isInvalidInput(): Boolean = this.errorType == ErrorTypes.INVALID_INPUT
16+
}
17+
18+
class RegisterStrongAuthSubmitChallengeError(
19+
override val errorType: String? = null,
20+
override val error: String? = null,
21+
override val errorMessage: String?,
22+
override val correlationId: String,
23+
override val errorCodes: List<Int>? = null,
24+
override var exception: Exception? = null
25+
): RegisterStrongAuthSubmitChallengeResult, Error(errorType = errorType, error = error, errorMessage= errorMessage, correlationId = correlationId, errorCodes = errorCodes, exception = exception)
26+
{
27+
fun isInvalidChallenge(): Boolean = this.errorType == ErrorTypes.INVALID_CHALLENGE
28+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// All rights reserved.
3+
//
4+
// This code is licensed under the MIT License.
5+
//
6+
// Permission is hereby granted, free of charge, to any person obtaining a copy
7+
// of this software and associated documentation files(the "Software"), to deal
8+
// in the Software without restriction, including without limitation the rights
9+
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
10+
// copies of the Software, and to permit persons to whom the Software is
11+
// furnished to do so, subject to the following conditions :
12+
//
13+
// The above copyright notice and this permission notice shall be included in
14+
// all copies or substantial portions of the Software.
15+
//
16+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
// THE SOFTWARE.
23+
24+
package com.microsoft.identity.nativeauth.statemachine.results
25+
26+
import com.microsoft.identity.nativeauth.parameters.NativeAuthRegisterStrongAuthVerificationRequiredResultParameter
27+
28+
interface RegisterStrongAuthChallengeResult: Result {
29+
30+
/**
31+
* Verification required result, which indicates that a challenge was sent to the user's auth method,
32+
* and the server expects the challenge to be verified.
33+
*
34+
* @param result [com.microsoft.identity.nativeauth.parameters.NativeAuthRegisterStrongAuthVerificationRequiredResultParameter] a parameter object containing the result of the action.
35+
*/
36+
class VerificationRequired(
37+
val result: NativeAuthRegisterStrongAuthVerificationRequiredResultParameter
38+
) : RegisterStrongAuthChallengeResult, Result.SuccessResult(nextState = result.nextState)
39+
40+
}
41+
42+
/**
43+
* Results related to submit challenge operation, produced by
44+
* [com.microsoft.identity.nativeauth.statemachine.states.RegisterStrongAuthVerificationquiredState.submitChallenge]
45+
*/
46+
interface RegisterStrongAuthSubmitChallengeResult : Result

msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/results/SignInResult.kt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@
2323

2424
package com.microsoft.identity.nativeauth.statemachine.results
2525

26+
import com.microsoft.identity.nativeauth.AuthMethod
2627
import com.microsoft.identity.nativeauth.statemachine.states.AccountState
2728
import com.microsoft.identity.nativeauth.statemachine.states.AwaitingMFAState
29+
import com.microsoft.identity.nativeauth.statemachine.states.RegisterStrongAuthState
2830
import com.microsoft.identity.nativeauth.statemachine.states.SignInCodeRequiredState
2931
import com.microsoft.identity.nativeauth.statemachine.states.SignInPasswordRequiredState
3032

@@ -45,7 +47,9 @@ interface SignInResult : Result {
4547
SignInResult,
4648
SignInSubmitCodeResult,
4749
SignInSubmitPasswordResult,
48-
MFASubmitChallengeResult
50+
MFASubmitChallengeResult,
51+
RegisterStrongAuthChallengeResult,
52+
RegisterStrongAuthSubmitChallengeResult
4953

5054
/**
5155
* CodeRequired Result, which indicates a verification code is required from the user to continue.
@@ -80,6 +84,17 @@ interface SignInResult : Result {
8084
class MFARequired(
8185
override val nextState: AwaitingMFAState
8286
) : SignInResult, Result.SuccessResult(nextState = nextState), SignInSubmitPasswordResult
87+
88+
/**
89+
* StrongAuthMethodRegistration Result, which indicates that a registration of a strong authentication method is required to continue.
90+
*
91+
* <strong><u>Warning: this class is experimental. It may be changed in the future without notice. Do not use in production applications.</u></strong>
92+
* @param nextState [com.microsoft.identity.nativeauth.statemachine.states.RegisterStrongAuthState] the current state of the flow with follow-on methods.
93+
*/
94+
class StrongAuthMethodRegistrationRequired(
95+
override val nextState: RegisterStrongAuthState,
96+
val authMethods: List<AuthMethod>
97+
) : SignInResult, SignInSubmitPasswordResult, Result.SuccessResult(nextState = nextState)
8398
}
8499

85100
/**

0 commit comments

Comments
 (0)