Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
51e714c to
1c6c787
Compare
36a65d6 to
daf9967
Compare
| internal data class Alternative( | ||
| @SerializedName("grant_type") | ||
| val grantType: String?, | ||
| val grantType: String, |
There was a problem hiding this comment.
Making grantType non-null looks risky. If the server sends an alternative without a grant_type, Gson will still shove null in here, and we end up building LoginOption.Unknown(rawGrantType = null) even though that field is non-null → NPE for consumers. We also lost the "just drop the entry" behavior (and its test). Can we keep String? and the null guard? Also this seems unrelated to the email-OTP feature.
There was a problem hiding this comment.
grant_type is a required field as per the spec. If it returns null value , thats a deviation from what the server is supposed to send
| } | ||
| private const val DEFAULT_SCOPE = "openid profile email offline_access" | ||
|
|
||
| private val DEFAULT_CAPABILITIES: Set<EmbeddedCapability> = |
There was a problem hiding this comment.
Default capabilities include identify:phone, but identifyPhone is internal and there's no phone flow yet. So the server could hand back a phone step the app literally can't act on. Can we default to just the email-OTP actions for this milestone?
|
|
||
| import com.auth0.android.embedded.EmbeddedAuthClient | ||
|
|
||
| /** How a phone one-time code is delivered, chosen when calling [EmbeddedAuthClient.challengePhone]. */ |
There was a problem hiding this comment.
This is public API for Flow 2, which isn't shipped yet — and the KDoc links to challengePhone, which doesn't exist. Once it's public we can't change it without a breaking release. Suggest keeping the phone/MFA types internal until that flow lands.
There was a problem hiding this comment.
User can't use this till the next change lands. Keeping this class as it is for now shouldn'tbe a problem for the beta changes
There was a problem hiding this comment.
Made this internal for now
| private val clientId: String | ||
| get() = auth0.clientId | ||
|
|
||
| private var transactionState: EmbeddedAuthState? = null |
There was a problem hiding this comment.
There was a problem hiding this comment.
Callbacks are usually returned on the main thread for us. But good to keep a safety check.
There was a problem hiding this comment.
Added @volatile annotation to make this visible across threads
| override val capability: EmbeddedCapability = EmbeddedCapability.IDENTIFY_EMAIL | ||
| } | ||
|
|
||
| /** Continue by submitting a phone number. Act on it with [EmbeddedAuthClient.identifyPhone]. */ |
There was a problem hiding this comment.
public type's KDoc points at identifyPhone, which is internal. (Goes away if we internalize the phone stuff.)
There was a problem hiding this comment.
Removed the reference for now
There was a problem hiding this comment.
All the new tests use .execute() — the await() (coroutine) branches in StepRequest/AdvancingRequest aren't covered. runTest is already imported; could we add one continuation + one terminal await() case?
| } | ||
| } | ||
| } | ||
| private const val DEFAULT_SCOPE = "openid profile email offline_access" |
There was a problem hiding this comment.
Default scope requests offline_access (i.e. a refresh token by default), but the spec has scope as optional. Just confirming that's intended.
| EmbeddedCapability.IDENTIFY_EMAIL -> NextAction.IdentifyEmail | ||
| EmbeddedCapability.IDENTIFY_PHONE -> NextAction.IdentifyPhone | ||
| EmbeddedCapability.CHALLENGE_EMAIL -> { | ||
| val index = (this[INDEX_KEY] as? Number)?.toInt() ?: return dropped(raw, INDEX_KEY) |
There was a problem hiding this comment.
If a required field is missing we drop the whole action — but if it was the only one, nextActions comes back empty and the user is stuck. Maybe surface it as Unknown instead so the menu still reflects what the server sent?
There was a problem hiding this comment.
This is intended. Returning an Unknown in this scenario can cause confusion whne we have multiple item with the incorrect field.
| .toString() | ||
| } | ||
|
|
||
| private fun updateSessionFromFailure(error: EmbeddedAuthException) { |
There was a problem hiding this comment.
A network error wipes the session here. Network failures come through as unknown_error, not insufficient_authorization, so they hit the else and clear transactionState. But the spec (and our own authorize.md example) says network errors are safe to retry the same step — after this, the retry fails with no_active_session and forces a full restart. Can we only clear on terminal access_denied and keep the session on transient errors?
There was a problem hiding this comment.
Yes. We can have check for transient errors for the same to ensure user is able to retry
| audience: String? = null, | ||
| capabilities: Set<EmbeddedCapability> = DEFAULT_CAPABILITIES | ||
| ): Request<Void?, EmbeddedAuthException> { | ||
| transactionState = null |
There was a problem hiding this comment.
Session gets read/cleared at construction, not execution. authorize() nulls the session the moment you build the request, and the step methods snapshot it at build time too. So client.authorize(...) kills an in-progress flow even if you never start it. Safer to do this when the request actually runs.
There was a problem hiding this comment.
Each authorize call is a start of a new login flow. So irrespective of it returns us a valid auth_session or not, we will clear the existing session to mark the termination of any ongoing flow
|
StepRequest.kt and AdvancingRequest.kt forward addParameter(name, String) to the inner request but don't override the addParameter(name, Any) overload, so it hits the interface's no-op default and silently drops the param. FailedRequest overrides it — worth making these consistent. |
| * to be usable. | ||
| */ | ||
| internal fun Alternative.toLoginOption(): LoginOption? { | ||
| val grantType = grantType ?: return null |
There was a problem hiding this comment.
in which case now login option can be null since we made grantType as non optional
There was a problem hiding this comment.
It can be null when other properties for a grant type return null when it is not expected to return null
| .addPathSegment(OAUTH_PATH) | ||
| .addPathSegment(TOKEN_PATH) | ||
| .build() | ||
| return factory.post(url.toString(), GsonAdapter(Credentials::class.java, gson)) |
There was a problem hiding this comment.
@pmathew92 in exchange dont we need to add id token verification?
There was a problem hiding this comment.
Good point. We can take it as a follow up separate PR and not club with this
Summary
EmbeddedAuthClientwith the full multi-step embedded/e/authorizeflow:authorize,identifyEmail,identifyPhone,challengeEmail,verifyOtpEmbeddedAuthExceptioncarrying a rotatedauth_sessionandnextActions;verifyOtpis the terminal step that yieldsCredentialsauthorize()now acceptsscope(default"openid profile email offline_access") and optionalaudienceso the token exchange returns anid_tokenauthorizeUrlto alazyproperty to avoid recomputing the URL on every step