@@ -2463,4 +2463,115 @@ public extension Auth {
2463
2463
self . tenantId = tenantId
2464
2464
}
2465
2465
}
2466
+
2467
+ /// Represents the result of a successful OIDC token exchange, containing a Firebase ID token
2468
+ /// and its expiration.
2469
+ struct FirebaseToken : Sendable {
2470
+ /// The Firebase ID token string.
2471
+ public let token : String
2472
+ /// The date at which the Firebase ID token expires.
2473
+ public let expirationDate : Date
2474
+
2475
+ init ( token: String , expirationDate: Date ) {
2476
+ self . token = token
2477
+ self . expirationDate = expirationDate
2478
+ }
2479
+ }
2480
+
2481
+ /// Exchanges a third-party OIDC token for a Firebase ID token.
2482
+ ///
2483
+ /// This method is used for Bring Your Own CIAM (BYO-CIAM) in Regionalized GCIP (R-GCIP),
2484
+ /// where the `Auth` instance must be configured with a `TenantConfig`, including `location`
2485
+ /// and `tenantId`, typically by using `Auth.auth(app:tenantConfig:)`.
2486
+ ///
2487
+ /// Unlike standard sign-in methods, this flow *does not* create or update a `User` object and
2488
+ /// *does not* set `CurrentUser` on the `Auth` instance. It only returns a Firebase token.
2489
+ ///
2490
+ /// - Parameters:
2491
+ /// - oidcToken: The OIDC ID token obtained from the third-party identity provider.
2492
+ /// - idpConfigId: The ID of the Identity Provider configuration within your GCIP tenant
2493
+ /// (e.g., "oidc.my-provider").
2494
+ /// - useStaging: A Boolean value indicating whether to use the staging Identity Platform
2495
+ /// backend. Defaults to `false`.
2496
+ /// - completion: A closure that is called asynchronously on the main thread with either a
2497
+ /// `FirebaseToken` on success or an `Error` on failure.
2498
+ func exchangeToken( idToken: String ,
2499
+ idpConfigId: String ,
2500
+ useStaging: Bool = false ,
2501
+ completion: @escaping ( FirebaseToken ? , Error ? ) -> Void ) {
2502
+ /// Ensure R-GCIP is configured with location and tenant ID
2503
+ guard let _ = requestConfiguration. tenantConfig? . location,
2504
+ let _ = requestConfiguration. tenantConfig? . tenantId
2505
+ else {
2506
+ Auth . wrapMainAsync (
2507
+ callback: completion,
2508
+ with: . failure( AuthErrorUtils
2509
+ . operationNotAllowedError (
2510
+ message: " Auth instance must be configured with a TenantConfig, including location and tenantId, to use exchangeToken. "
2511
+ ) )
2512
+ )
2513
+ return
2514
+ }
2515
+ let request = ExchangeTokenRequest (
2516
+ idToken: idToken,
2517
+ idpConfigID: idpConfigId,
2518
+ config: requestConfiguration,
2519
+ useStaging: true
2520
+ )
2521
+ Task {
2522
+ do {
2523
+ let response = try await backend. call ( with: request)
2524
+ let firebaseToken = FirebaseToken (
2525
+ token: response. firebaseToken,
2526
+ expirationDate: response. expirationDate
2527
+ )
2528
+ Auth . wrapMainAsync ( callback: completion, with: . success( firebaseToken) )
2529
+ } catch {
2530
+ Auth . wrapMainAsync ( callback: completion, with: . failure( error) )
2531
+ }
2532
+ }
2533
+ }
2534
+
2535
+ /// Exchanges a third-party OIDC ID token for a Firebase ID token using Swift concurrency.
2536
+ ///
2537
+ /// This method is used for Bring Your Own CIAM (BYO-CIAM) in Regionalized GCIP (R-GCIP),
2538
+ /// where the `Auth` instance must be configured with a `TenantConfig`, including `location`
2539
+ /// and `tenantId`, typically by using `Auth.auth(app:tenantConfig:)`.
2540
+ ///
2541
+ /// Unlike standard sign-in methods, this flow *does not* create or update a `User`object and
2542
+ /// *does not* set `CurrentUser` on the `Auth` instance. It only returns a Firebase token.
2543
+ ///
2544
+ /// - Parameters:
2545
+ /// - oidcToken: The OIDC ID token obtained from the third-party identity provider.
2546
+ /// - idpConfigId: The ID of the Identity Provider configuration within your GCIP tenant
2547
+ /// (e.g., "oidc.my-provider").
2548
+ /// - useStaging: A Boolean value indicating whether to use the staging Identity Platform
2549
+ /// backend. Defaults to `false`.
2550
+ /// - Returns: A `FirebaseToken` containing the Firebase ID token and its expiration date.
2551
+ /// - Throws: An error if the `Auth` instance is not configured for R-GCIP, if the network
2552
+ /// call fails, or if the token response parsing fails.
2553
+ func exchangeToken( idToken: String , idpConfigId: String ,
2554
+ useStaging: Bool = false ) async throws -> FirebaseToken {
2555
+ // Ensure R-GCIP is configured with location and tenant ID
2556
+ guard let _ = requestConfiguration. tenantConfig? . location,
2557
+ let _ = requestConfiguration. tenantConfig? . tenantId
2558
+ else {
2559
+ throw AuthErrorUtils . operationNotAllowedError ( message: " R-GCIP is not configured. " )
2560
+ }
2561
+ let request = ExchangeTokenRequest (
2562
+ idToken: idToken,
2563
+ idpConfigID: idpConfigId,
2564
+ config: requestConfiguration,
2565
+ useStaging: true
2566
+ )
2567
+ do {
2568
+ let response = try await backend. call ( with: request)
2569
+ return FirebaseToken (
2570
+ token: response. firebaseToken,
2571
+ expirationDate: response. expirationDate
2572
+ )
2573
+ } catch {
2574
+ throw error
2575
+ }
2576
+ }
2466
2577
}
0 commit comments