resolveOAuthCallbackSignInForProvider<TContext, TProfile extends Object> function

Future<AuthOAuthCallbackSignInResolution> resolveOAuthCallbackSignInForProvider<TContext, TProfile extends Object>({
  1. required AuthStore store,
  2. required TContext context,
  3. required OAuthProvider<TProfile> provider,
  4. required String code,
  5. required String? receivedState,
  6. required String stateKey,
  7. required String pkceKey,
  8. required String callbackKey,
  9. required String? readSession(
    1. String key
    ),
  10. required Client httpClient,
  11. String nonceKey = '_auth.nonce',
  12. void removeSession(
    1. String key
    )?,
  13. FutureOr<AuthOAuthChallenge?> consumeChallenge(
    1. String providerId,
    2. String state
    )?,
  14. String? expectedBrowserState,
  15. bool requireBrowserState = false,
  16. String fallbackAccountId()?,
})

Resolves a full OAuth callback flow including state validation and account linking.

Implementation

Future<AuthOAuthCallbackSignInResolution>
resolveOAuthCallbackSignInForProvider<TContext, TProfile extends Object>({
  required AuthStore store,
  required TContext context,
  required OAuthProvider<TProfile> provider,
  required String code,
  required String? receivedState,
  required String stateKey,
  required String pkceKey,
  required String callbackKey,
  required String? Function(String key) readSession,
  required http.Client httpClient,
  String nonceKey = '_auth.nonce',
  void Function(String key)? removeSession,
  FutureOr<AuthOAuthChallenge?> Function(String providerId, String state)?
  consumeChallenge,

  /// A browser-bound copy of the received state, supplied by the framework
  /// adapter (for example from an HttpOnly state cookie).
  String? expectedBrowserState,

  /// Requires the adapter to provide a browser-bound state value. This keeps
  /// a durable challenge store from accepting a callback initiated by another
  /// browser.
  bool requireBrowserState = false,
  String Function()? fallbackAccountId,
}) async {
  if (requireBrowserState) {
    // Reject an unbound callback before consuming the durable challenge. An
    // attacker must not be able to turn a failed login-CSRF attempt into a
    // denial of service for the browser that started the OAuth flow.
    ensureOAuthStateMatches(
      expectedState: expectedBrowserState,
      receivedState: receivedState,
    );
  }

  final AuthOAuthCallbackSessionValues sessionValues;
  if (consumeChallenge != null) {
    final challenge = receivedState == null
        ? null
        : await Future.sync(() => consumeChallenge(provider.id, receivedState));
    if (challenge == null) {
      throw AuthFlowException('invalid_state');
    }
    sessionValues = AuthOAuthCallbackSessionValues(
      expectedState: challenge.state,
      codeVerifier: challenge.codeVerifier,
      nonce: challenge.nonce,
      callbackUrl: challenge.callbackUrl,
    );
    // The durable challenge is the replay guard, but the mirrored framework
    // state should be removed from the browser session once that challenge is
    // consumed as well.
    removeSession?.call(authProviderStateSessionKey(stateKey, provider.id));
  } else {
    sessionValues = resolveOAuthCallbackSessionValues(
      providerId: provider.id,
      stateKey: stateKey,
      pkceKey: pkceKey,
      nonceKey: nonceKey,
      callbackKey: callbackKey,
      readSession: readSession,
    );
    ensureOAuthStateMatches(
      expectedState: sessionValues.expectedState,
      receivedState: receivedState,
    );

    removeSession?.call(authProviderStateSessionKey(stateKey, provider.id));
    removeSession?.call(authProviderPkceSessionKey(pkceKey, provider.id));
    removeSession?.call(authProviderNonceSessionKey(nonceKey, provider.id));
    removeSession?.call(
      authProviderCallbackSessionKey(callbackKey, provider.id),
    );
  }
  ensureOAuthStateMatches(
    expectedState: sessionValues.expectedState,
    receivedState: receivedState,
  );
  final signIn = await resolveOAuthSignInForProvider<TContext, TProfile>(
    store: store,
    context: context,
    provider: provider,
    code: code,
    codeVerifier: sessionValues.codeVerifier,
    oidcNonce: sessionValues.nonce,
    httpClient: httpClient,
    fallbackAccountId: fallbackAccountId,
  );

  return AuthOAuthCallbackSignInResolution(
    signIn: signIn,
    callbackUrl: sessionValues.callbackUrl,
  );
}