Native Auth V2: Sign-up - #2565
Conversation
|
❌ Work item link check failed. Description does not contain AB#{ID}. Click here to Learn more. |
…-auth-v2-signup # Conflicts: # common
Cross-platform review: CIAM Native Auth V2 sign-up public surface (Android MSAL ↔ MSAL iOS
|
| Entry point | NativeAuthV2CommandResult.AttributesInvalid maps to |
|---|---|
AttributesRequiredStateV2 / AttributesInvalidStateV2.submitAttributes (via submitAttributesInternal) |
NativeAuthResultV2.AttributesInvalid — retryable, carries invalidAttributes, exposes a live AttributesInvalidStateV2 |
PasswordRequiredStateV2.submitSignUpPassword |
SubmitPasswordErrorV2(INVALID_PASSWORD) — terminal error, result.invalidAttributes discarded |
Impact. The collapse to INVALID_PASSWORD is reasonable given the assumption that only password was submitted, and isInvalidPassword does let the app retry on the same state. But nothing enforces that assumption: InvalidAttributes.invalidAttributes in the common layer is a flatMap over all innerError.details[].attributeIds (see common#3240 finding 8), so it can legitimately name attributes other than password. When it does, the app is told its password was invalid and loses the real list.
Recommendation. Either surface NativeAuthResultV2.AttributesInvalid consistently from submitSignUpPassword, or branch on whether invalidAttributes is exactly ["password"] and fall through to the generic path otherwise. At minimum, document the assumption in the KDoc.
5. Severity: Medium — NativeAuthResultV2.AttributesInvalid has no iOS counterpart
Issue. iOS's V2 parser never emits an attributes-invalid result — MSALNativeAuthAttributesInvalidState exists as a class but nothing in the V2 code path constructs it (the attributeValidationFailed handling on iOS is V1-only). The mapping that drives this result (attributeValidationError on error.innerError.code) is Android-only.
Impact. A password-policy violation on submit is an actionable, retryable public result on Android and an opaque general error on iOS. That's the largest divergence in the public contract, and it means "Mirror the iOS V2 sign-up scenarios" in the PR description isn't accurate for this branch.
Recommendation. Keep the Android behaviour and file the iOS gap, or hold it until iOS lands the equivalent — either way, call it out in the PR description so the platforms don't drift silently. Same note as common#3240 finding 3.
6. Severity: Medium — reserved attribute names are dropped with no signal to the app
Issue. email and password supplied in NativeAuthSignUpParameters.attributes are filtered out in the common controller with only a Logger.warn. signUpV2's KDoc doesn't mention the reserved names, so an app that sets attributes = mapOf("email" to ...) gets no compile-time, runtime, or documentation signal that its value was ignored.
Separately, AttributesRequiredStateV2.submitAttributes and AttributesInvalidStateV2.submitAttributes apply no reserved-name filtering at all, so an app can re-send email/password mid-flow through the deferred states, bypassing the upfront guard. iOS is identical here (submitAttributes(_:state:) posts the dictionary verbatim), so that half is parity — noting it for the record.
Recommendation. Document the reserved names on NativeAuthSignUpParameters.attributes and on both submitAttributes overloads. Consider surfacing an error instead of silently dropping.
7. Severity: Medium — public attribute values are Map<String, String>; iOS accepts arbitrary JSON
Issue. The public attributes parameter is string-valued end to end. iOS's MSALNativeAuthV2SubmitAttributesRequestBody.attributes is [String: Any] (guarded by JSONSerialization.isValidJSONObject), so a numeric or boolean CIAM extension attribute serializes with its native JSON type.
Impact. extension_age: 30 goes out as "30" from Android and 30 from iOS. If the directory schema types that attribute as a number, one platform is rejected.
Assumption: this may be a deliberate Android-wide constraint carried over from V1 UserAttributes, in which case disregard — but please confirm the v2 submitAttributes contract coerces stringified values for non-string schema types. Same note as common#3240 finding 6.
8. Non-blocking — RequiredUserAttribute.required is Boolean? where iOS is non-optional
iOS coerces a missing required to false (attribute["required"] as? Bool ?? false). Android carries Boolean? through NativeAuthV2RequiredAttribute to the public RequiredUserAttribute.required, so apps see null on Android where iOS sees false. RequiredUserAttribute is a pre-existing V1 type shared with the V1 flow, so changing it isn't in scope — the V2 mapper could default it instead.
Cycle summary
- New issues: 8 (1 High, 6 Medium, 1 non-blocking)
- Resolved issues: —
- Remaining blockers: Add HttpComponent for MSAL #1 —
signUpV2cannot complete an OTP-free password sign-up; the account is created server-side but the app gets a generic error and no continuation state. Blocked on the marker-interface fix in common#3240.
Verified as not issues, for the record: writeToParcel / parcel-constructor field ordering in AttributesRequiredStateV2, AttributesInvalidStateV2, and SignInAfterSignUpStateV2 is symmetric with NativeAuthBaseStateV2; routing the sign-up password through submitAttributes rather than the sign-in submit-password endpoint matches iOS; and the new CodeRequiredStateV2.submitCode branches (AttributesRequired / PasswordRequired / SignInAfterSignUpRequired) correctly cover the widened NativeAuthV2SubmitCodeCommandResult.
Summary
Adds the msal public API layer for Native Auth V2 sign-up (
signUpV2), alongside the existing V2 sign-in and SSPR support.Mirrors the iOS V2 sign-up scenarios (AzureAD/microsoft-authentication-library-for-objc#3093) and follows the established V2 sign-in / SSPR public-surface shape:
signUpV2entry point + unifiedNativeAuthResultV2results (CodeRequired, AttributesRequired/Invalid, Complete) andSignUpErrorV2taxonomy.Tests
NativeAuthV2SignUpTestplus updated interface/state/error tests.Dependency
Pairs with common PR AzureAD/microsoft-authentication-library-common-for-android#3240 (bumped via the
commonsubmodule).Draft.
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com