Skip to content

Commit c1eafb7

Browse files
authored
Native auth: Add support for claims request, Fixes AB#3117218 (#2246)
Adding support on native authentication for claims request. During sign-in, native authentication users can now specify claims requests, which will be sent to the /token endpoint. This enhancement specifically supports authentication context. MSAL Common PR: AzureAD/microsoft-authentication-library-common-for-android#2572 [AB#3117218](https://identitydivision.visualstudio.com/Engineering/_workitems/edit/3117218)
1 parent bcda2d7 commit c1eafb7

File tree

7 files changed

+142
-17
lines changed

7 files changed

+142
-17
lines changed

changelog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ MSAL Wiki : https://github.com/AzureAD/microsoft-authentication-library-for-andr
33
vNext
44
----------
55
- [MINOR] Move native auth public methods to parameter class (#2245)
6+
- [MINOR] Add support for claims requests for native auth sign in (#2246)
67

78
Version 5.9.0
89
----------

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -539,14 +539,17 @@ public static SignInStartCommandParameters createSignInStartCommandParameters(
539539
@NonNull final OAuth2TokenCache tokenCache,
540540
@NonNull final String username,
541541
@Nullable final char[] password,
542-
final List<String> scopes) throws ClientException {
542+
final List<String> scopes,
543+
@Nullable final ClaimsRequest claimsRequest) throws ClientException {
543544
final AbstractAuthenticationScheme authenticationScheme = AuthenticationSchemeFactory.createScheme(
544545
AndroidPlatformComponentsFactory.createFromContext(configuration.getAppContext()),
545546
null
546547
);
547548

548549
final NativeAuthCIAMAuthority authority = ((NativeAuthCIAMAuthority) configuration.getDefaultAuthority());
549550

551+
final String claimsRequestJson = ClaimsRequest.getJsonStringFromClaimsRequest(claimsRequest);
552+
550553
final SignInStartCommandParameters commandParameters = SignInStartCommandParameters.builder()
551554
.platformComponents(AndroidPlatformComponentsFactory.createFromContext(configuration.getAppContext()))
552555
.applicationName(configuration.getAppContext().getPackageName())
@@ -565,6 +568,7 @@ public static SignInStartCommandParameters createSignInStartCommandParameters(
565568
.authenticationScheme(authenticationScheme)
566569
.clientId(configuration.getClientId())
567570
.challengeType(configuration.getChallengeTypes())
571+
.claimsRequestJson(claimsRequestJson)
568572
.scopes(scopes)
569573
// Start of the flow, so there is no correlation ID to use from a previous API response.
570574
// Set it to a default value.
@@ -640,7 +644,8 @@ public static SignInSubmitCodeCommandParameters createSignInSubmitCodeCommandPar
640644
@NonNull final String code,
641645
@NonNull final String continuationToken,
642646
@NonNull final String correlationId,
643-
final List<String> scopes) throws ClientException {
647+
final List<String> scopes,
648+
@Nullable final String claimsRequestJson) throws ClientException {
644649

645650
final NativeAuthCIAMAuthority authority = ((NativeAuthCIAMAuthority) configuration.getDefaultAuthority());
646651

@@ -668,6 +673,7 @@ public static SignInSubmitCodeCommandParameters createSignInSubmitCodeCommandPar
668673
.code(code)
669674
.scopes(scopes)
670675
.correlationId(correlationId)
676+
.claimsRequestJson(claimsRequestJson)
671677
.build();
672678

673679
return commandParameters;
@@ -729,7 +735,8 @@ public static SignInSubmitPasswordCommandParameters createSignInSubmitPasswordCo
729735
@NonNull final String continuationToken,
730736
@NonNull final char[] password,
731737
@NonNull final String correlationId,
732-
final List<String> scopes) throws ClientException {
738+
final List<String> scopes,
739+
@Nullable final String claimsRequestJson) throws ClientException {
733740

734741
final NativeAuthCIAMAuthority authority = ((NativeAuthCIAMAuthority) configuration.getDefaultAuthority());
735742

@@ -758,6 +765,7 @@ public static SignInSubmitPasswordCommandParameters createSignInSubmitPasswordCo
758765
.scopes(scopes)
759766
.challengeType(configuration.getChallengeTypes())
760767
.correlationId(correlationId)
768+
.claimsRequestJson(claimsRequestJson)
761769
.build();
762770

763771
return commandParameters;

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

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import com.microsoft.identity.client.AccountAdapter
2828
import com.microsoft.identity.client.AuthenticationResultAdapter
2929
import com.microsoft.identity.client.IAccount
3030
import com.microsoft.identity.client.PublicClientApplication
31+
import com.microsoft.identity.client.claims.ClaimsRequest
3132
import com.microsoft.identity.client.exception.MsalClientException
3233
import com.microsoft.identity.client.exception.MsalException
3334
import com.microsoft.identity.client.internal.CommandParametersAdapter
@@ -309,7 +310,7 @@ class NativeAuthPublicClientApplication(
309310
)
310311
pcaScope.launch {
311312
try {
312-
val result = internalSignIn(username, password, scopes)
313+
val result = internalSignIn(username, password, scopes, claimsRequest = null)
313314
callback.onResult(result)
314315
} catch (e: MsalException) {
315316
Logger.error(TAG, "Exception thrown in signIn", e)
@@ -333,7 +334,7 @@ class NativeAuthPublicClientApplication(
333334
)
334335
pcaScope.launch {
335336
try {
336-
val result = internalSignIn(parameters.username, parameters.password, parameters.scopes)
337+
val result = internalSignIn(parameters.username, parameters.password, parameters.scopes, parameters.claimsRequest)
337338
callback.onResult(result)
338339
} catch (e: MsalException) {
339340
Logger.error(TAG, "Exception thrown in signIn", e)
@@ -362,7 +363,7 @@ class NativeAuthPublicClientApplication(
362363
correlationId = null,
363364
methodName = "${TAG}.signIn(username: String, password: CharArray?, scopes: List<String>?)"
364365
)
365-
return internalSignIn(username, password, scopes)
366+
return internalSignIn(username, password, scopes, claimsRequest = null)
366367
}
367368

368369
/**
@@ -378,7 +379,7 @@ class NativeAuthPublicClientApplication(
378379
correlationId = null,
379380
methodName = "${TAG}.signIn(parameters: NativeAuthSignInParameters)"
380381
)
381-
return internalSignIn(parameters.username, parameters.password, parameters.scopes)
382+
return internalSignIn(parameters.username, parameters.password, parameters.scopes, parameters.claimsRequest)
382383
}
383384

384385

@@ -605,7 +606,8 @@ class NativeAuthPublicClientApplication(
605606
private suspend fun internalSignIn(
606607
username: String,
607608
password: CharArray?,
608-
scopes: List<String>?
609+
scopes: List<String>?,
610+
claimsRequest: ClaimsRequest?
609611
): SignInResult {
610612
return withContext(Dispatchers.IO) {
611613
try {
@@ -627,13 +629,14 @@ class NativeAuthPublicClientApplication(
627629
nativeAuthConfig.oAuth2TokenCache,
628630
username,
629631
password,
630-
scopes
632+
scopes,
633+
claimsRequest
631634
)
632635

633636
val command = SignInStartCommand(
634637
params,
635638
NativeAuthMsalController(),
636-
PublicApiId.NATIVE_AUTH_SIGN_IN_WITH_EMAIL
639+
if (hasPassword) PublicApiId.NATIVE_AUTH_SIGN_IN_WITH_EMAIL_PASSWORD else PublicApiId.NATIVE_AUTH_SIGN_IN_WITH_EMAIL
637640
)
638641

639642
val rawCommandResult = CommandDispatcher.submitSilentReturningFuture(command).get()
@@ -679,7 +682,8 @@ class NativeAuthPublicClientApplication(
679682
continuationToken = result.continuationToken,
680683
correlationId = result.correlationId,
681684
scopes = scopes,
682-
config = nativeAuthConfig
685+
config = nativeAuthConfig,
686+
claimsRequestJson = params.claimsRequestJson
683687
),
684688
codeLength = result.codeLength,
685689
sentTo = result.challengeTargetLabel,
@@ -715,7 +719,8 @@ class NativeAuthPublicClientApplication(
715719
continuationToken = result.continuationToken,
716720
correlationId = result.correlationId,
717721
scopes = scopes,
718-
config = nativeAuthConfig
722+
config = nativeAuthConfig,
723+
claimsRequestJson = params.claimsRequestJson
719724
)
720725
)
721726
}

msal/src/main/java/com/microsoft/identity/nativeauth/parameters/NativeAuthSignInParameters.kt

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

2424
package com.microsoft.identity.nativeauth.parameters
2525

26+
import com.microsoft.identity.client.claims.ClaimsRequest
27+
2628
/**
2729
* Encapsulates the parameters passed to the signIn methods of NativeAuthPublicClientApplication
2830
*/
2931
class NativeAuthSignInParameters(
3032
/**
31-
* username of the account to sign in.
33+
* username of the account to sign in
3234
*/
3335
val username: String
3436
) {
@@ -43,4 +45,9 @@ class NativeAuthSignInParameters(
4345
* Not all scopes are guaranteed to be included in the access token returned.
4446
*/
4547
var scopes: List<String>? = null
48+
49+
/**
50+
* The claims parameter that needs to be sent to the service.
51+
*/
52+
var claimsRequest: ClaimsRequest? = null
4653
}

msal/src/main/java/com/microsoft/identity/nativeauth/statemachine/states/SignInStates.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class SignInCodeRequiredState internal constructor(
7676
override val continuationToken: String,
7777
override val correlationId: String,
7878
private val scopes: List<String>?,
79+
private val claimsRequestJson: String?,
7980
private val config: NativeAuthPublicClientApplicationConfiguration
8081
) : BaseState(continuationToken = continuationToken, correlationId = correlationId), State, Parcelable {
8182
private val TAG: String = SignInCodeRequiredState::class.java.simpleName
@@ -84,6 +85,7 @@ class SignInCodeRequiredState internal constructor(
8485
continuationToken = parcel.readString() ?: "",
8586
correlationId = parcel.readString() ?: "UNSET",
8687
scopes = parcel.createStringArrayList(),
88+
claimsRequestJson = parcel.readString(),
8789
config = parcel.serializable<NativeAuthPublicClientApplicationConfiguration>() as NativeAuthPublicClientApplicationConfiguration
8890
)
8991

@@ -136,7 +138,8 @@ class SignInCodeRequiredState internal constructor(
136138
code,
137139
continuationToken,
138140
correlationId,
139-
scopes
141+
scopes,
142+
claimsRequestJson
140143
)
141144

142145
val signInSubmitCodeCommand = SignInSubmitCodeCommand(
@@ -274,7 +277,8 @@ class SignInCodeRequiredState internal constructor(
274277
continuationToken = result.continuationToken,
275278
correlationId = result.correlationId,
276279
scopes = scopes,
277-
config = config
280+
config = config,
281+
claimsRequestJson = claimsRequestJson
278282
),
279283
codeLength = result.codeLength,
280284
sentTo = result.challengeTargetLabel,
@@ -344,13 +348,15 @@ class SignInPasswordRequiredState(
344348
override val continuationToken: String,
345349
override val correlationId: String,
346350
private val scopes: List<String>?,
351+
private val claimsRequestJson: String?,
347352
private val config: NativeAuthPublicClientApplicationConfiguration
348353
) : BaseState(continuationToken = continuationToken, correlationId = correlationId), State, Parcelable {
349354
private val TAG: String = SignInPasswordRequiredState::class.java.simpleName
350355
constructor(parcel: Parcel) : this(
351356
continuationToken = parcel.readString() ?: "",
352357
correlationId = parcel.readString() ?: "UNSET",
353358
scopes = parcel.createStringArrayList(),
359+
claimsRequestJson = parcel.readString(),
354360
config = parcel.serializable<NativeAuthPublicClientApplicationConfiguration>() as NativeAuthPublicClientApplicationConfiguration
355361
)
356362

@@ -403,7 +409,8 @@ class SignInPasswordRequiredState(
403409
continuationToken,
404410
password,
405411
correlationId,
406-
scopes
412+
scopes,
413+
claimsRequestJson
407414
)
408415

409416
try {
@@ -483,6 +490,7 @@ class SignInPasswordRequiredState(
483490
parcel.writeString(correlationId)
484491
parcel.writeStringList(scopes)
485492
parcel.writeSerializable(config)
493+
parcel.writeString(claimsRequestJson)
486494
}
487495

488496
override fun describeContents(): Int {

msal/src/test/java/com/microsoft/identity/client/CommandParametersTest.java

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,15 @@
4141
import com.microsoft.identity.common.java.commands.parameters.InteractiveTokenCommandParameters;
4242
import com.microsoft.identity.common.java.commands.parameters.SilentTokenCommandParameters;
4343
import com.microsoft.identity.common.java.constants.FidoConstants;
44+
import com.microsoft.identity.common.java.nativeauth.commands.parameters.SignInStartCommandParameters;
45+
import com.microsoft.identity.common.java.nativeauth.commands.parameters.SignInSubmitCodeCommandParameters;
46+
import com.microsoft.identity.common.java.nativeauth.commands.parameters.SignInSubmitPasswordCommandParameters;
4447
import com.microsoft.identity.common.java.providers.oauth2.OAuth2TokenCache;
4548
import com.microsoft.identity.common.java.exception.ClientException;
4649
import com.microsoft.identity.common.java.ui.PreferredAuthMethod;
50+
import com.microsoft.identity.msal.R;
51+
import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplicationConfiguration;
52+
import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplicationConfigurationFactory;
4753

4854
import org.junit.Assert;
4955
import org.junit.Before;
@@ -358,6 +364,96 @@ public void testAppendToExtraQueryParametersIfWebAuthnCapable_WebAuthnCapableFal
358364
Assert.assertEquals(combinedQueryParameters.size(), 1);
359365
}
360366

367+
@Test
368+
public void testCreateSignInStartCommandParameters_CommandParamsContainsExpectedParams() throws ClientException {
369+
List<String> challengeTypes = new ArrayList<>(Collections.singletonList("OOB"));
370+
String username = "username";
371+
char[] pwd = "example".toCharArray();
372+
List<String> scopes = new ArrayList<>(Collections.singletonList("User.Read"));
373+
ClaimsRequest claimsRequest = ClaimsRequest.getClaimsRequestFromJsonString("{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"c4\"}}}");
374+
NativeAuthPublicClientApplicationConfiguration configuration = new NativeAuthPublicClientApplicationConfiguration();
375+
configuration.setChallengeTypes(challengeTypes);
376+
configuration.setClientId("clientId");
377+
configuration.setAppContext(mContext);
378+
configuration.setPowerOptCheckEnabled(false);
379+
380+
final SignInStartCommandParameters commandParameters = CommandParametersAdapter.createSignInStartCommandParameters(
381+
configuration,
382+
null,
383+
username,
384+
pwd,
385+
scopes,
386+
claimsRequest
387+
);
388+
Assert.assertEquals(commandParameters.claimsRequestJson, ClaimsRequest.getJsonStringFromClaimsRequest(claimsRequest));
389+
Assert.assertEquals(commandParameters.password, pwd);
390+
Assert.assertEquals(commandParameters.username, username);
391+
Assert.assertEquals(commandParameters.scopes, scopes);
392+
Assert.assertEquals(commandParameters.challengeType, challengeTypes);
393+
}
394+
395+
@Test
396+
public void createSignInSubmitCodeCommandParameters_CommandParamsContainsExpectedParams() throws ClientException {
397+
List<String> challengeTypes = new ArrayList<>(Collections.singletonList("OOB"));
398+
String code = "123456";
399+
String continuationToken = "continuationToken";
400+
String correlationId = UUID.randomUUID().toString();
401+
List<String> scopes = new ArrayList<>(Collections.singletonList("User.Read"));
402+
String claimsRequestJson = "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"c4\"}}}";
403+
NativeAuthPublicClientApplicationConfiguration configuration = new NativeAuthPublicClientApplicationConfiguration();
404+
configuration.setChallengeTypes(challengeTypes);
405+
configuration.setClientId("clientId");
406+
configuration.setAppContext(mContext);
407+
configuration.setPowerOptCheckEnabled(false);
408+
409+
final SignInSubmitCodeCommandParameters commandParameters = CommandParametersAdapter.createSignInSubmitCodeCommandParameters(
410+
configuration,
411+
null,
412+
code,
413+
continuationToken,
414+
correlationId,
415+
scopes,
416+
claimsRequestJson
417+
);
418+
Assert.assertEquals(commandParameters.claimsRequestJson, claimsRequestJson);
419+
Assert.assertEquals(commandParameters.code, code);
420+
Assert.assertEquals(commandParameters.continuationToken, continuationToken);
421+
Assert.assertEquals(commandParameters.scopes, scopes);
422+
Assert.assertEquals(commandParameters.challengeType, challengeTypes);
423+
Assert.assertEquals(commandParameters.getCorrelationId(), correlationId);
424+
}
425+
426+
@Test
427+
public void createSignInSubmitPasswordCommandParameters_CommandParamsContainsExpectedParams() throws ClientException {
428+
List<String> challengeTypes = new ArrayList<>(Collections.singletonList("OOB"));
429+
char[] pwd = "example".toCharArray();
430+
String continuationToken = "continuationToken";
431+
String correlationId = UUID.randomUUID().toString();
432+
List<String> scopes = new ArrayList<>(Collections.singletonList("User.Read"));
433+
String claimsRequestJson = "{\"access_token\":{\"acrs\":{\"essential\":true,\"value\":\"c4\"}}}";
434+
NativeAuthPublicClientApplicationConfiguration configuration = new NativeAuthPublicClientApplicationConfiguration();
435+
configuration.setChallengeTypes(challengeTypes);
436+
configuration.setClientId("clientId");
437+
configuration.setAppContext(mContext);
438+
configuration.setPowerOptCheckEnabled(false);
439+
440+
final SignInSubmitPasswordCommandParameters commandParameters = CommandParametersAdapter.createSignInSubmitPasswordCommandParameters(
441+
configuration,
442+
null,
443+
continuationToken,
444+
pwd,
445+
correlationId,
446+
scopes,
447+
claimsRequestJson
448+
);
449+
Assert.assertEquals(commandParameters.claimsRequestJson, claimsRequestJson);
450+
Assert.assertEquals(commandParameters.password, pwd);
451+
Assert.assertEquals(commandParameters.continuationToken, continuationToken);
452+
Assert.assertEquals(commandParameters.scopes, scopes);
453+
Assert.assertEquals(commandParameters.challengeType, challengeTypes);
454+
Assert.assertEquals(commandParameters.getCorrelationId(), correlationId);
455+
}
456+
361457
private ClaimsRequest getAccessTokenClaimsRequest(@NonNull String claimName, @NonNull String claimValue) {
362458
ClaimsRequest cp1ClaimsRequest = new ClaimsRequest();
363459
RequestedClaimAdditionalInformation info = new RequestedClaimAdditionalInformation();

0 commit comments

Comments
 (0)