@@ -189,25 +189,25 @@ extension Auth: AuthInterop {
189
189
}
190
190
191
191
/// Holds configuration for a R-GCIP tenant.
192
- public struct TenantConfig {
193
- public let location : String /// The location of the tenant.
192
+ public struct TenantConfig : Sendable {
194
193
public let tenantId : String /// The ID of the tenant.
194
+ public let location : String /// The location of the tenant.
195
195
196
196
/// Initializes a `TenantConfig` instance.
197
197
/// - Parameters:
198
198
/// - location: The location of the tenant, defaults to "prod-global".
199
199
/// - tenantId: The ID of the tenant.
200
- public init ( location: String = " prod-global " , tenantId : String ) {
200
+ public init ( tenantId : String , location: String = " prod-global " ) {
201
201
self . location = location
202
202
self . tenantId = tenantId
203
203
}
204
204
}
205
205
206
206
/// Holds a Firebase ID token and its expiration.
207
- public struct AuthExchangeToken {
207
+ public struct AuthExchangeToken : Sendable {
208
208
public let token : String
209
209
public let expirationDate : Date ?
210
- init ( token: String , expirationDate: Date ? ) {
210
+ init ( token: String , expirationDate: Date ) {
211
211
self . token = token
212
212
self . expirationDate = expirationDate
213
213
}
@@ -2471,37 +2471,91 @@ extension Auth: AuthInterop {
2471
2471
2472
2472
@available ( iOS 13 , * )
2473
2473
public extension Auth {
2474
- /// Exchanges a third-party OIDC token for a Firebase STS token.
2474
+
2475
+ /// Exchanges a third-party OIDC token for a Firebase STS token using a completion handler.
2476
+ ///
2477
+ /// This method is used in R-GCIP (multi-tenant) environments where the `Auth` instance must
2478
+ /// be configured with a `TenantConfig`, including `location` and `tenantId`.
2475
2479
///
2476
- /// Requires the `Auth` instance to be configured with a `TenantConfig` for R-GCIP.
2477
2480
/// - Parameters:
2478
- /// - idpConfigID: The ID of the OIDC provider configuration.
2479
- /// - ciamOidcToken: The OIDC token to exchange.
2480
- /// - completion: Called with the Firebase ID token or an error.
2481
- @objc func exchangeToken( _ idpConfigID: String ,
2482
- _ ciamOidcToken: String ,
2483
- completion: @escaping ( String ? , Error ? ) -> Void ) {
2484
- // Check if R-GCIP (location and tenantId) is configured.
2485
- guard let location = requestConfiguration. location,
2486
- let tenantId = requestConfiguration. tenantId
2487
- else {
2488
- completion ( nil , AuthErrorUtils . operationNotAllowedError (
2489
- message: " Set location & tenantId first "
2490
- ) )
2491
- return
2492
- }
2493
- let request = ExchangeTokenRequest (
2494
- idpConfigID: idpConfigID,
2495
- idToken: ciamOidcToken,
2496
- config: requestConfiguration
2497
- )
2498
- Task {
2499
- do {
2500
- let resp = try await backend. call ( with: request)
2501
- DispatchQueue . main. async { completion ( resp. firebaseToken, nil ) }
2502
- } catch {
2503
- DispatchQueue . main. async { completion ( nil , error) }
2481
+ /// - idToken: The OIDC token received from the third-party Identity Provider (IdP).
2482
+ /// - idpConfigId: The identifier of the OIDC provider configuration defined in Firebase.
2483
+ /// - completion: A closure that gets called with either an `AuthTokenResult` or an `Error`.
2484
+ public func exchangeToken(
2485
+ idToken: String ,
2486
+ idpConfigId: String ,
2487
+ completion: @escaping ( AuthTokenResult ? , Error ? ) -> Void
2488
+ ) {
2489
+ // Ensure R-GCIP is configured with location and tenant ID
2490
+ guard let location = requestConfiguration. location,
2491
+ let tenantId = requestConfiguration. tenantId
2492
+ else {
2493
+ completion ( nil , AuthErrorCode . operationNotAllowed)
2494
+ return
2504
2495
}
2505
- }
2496
+
2497
+ // Build the exchange token request
2498
+ let request = ExchangeTokenRequest (
2499
+ idToken: idToken,
2500
+ idpConfigID: idpConfigId,
2501
+ config: requestConfiguration
2502
+ )
2503
+
2504
+ // Perform the token exchange asynchronously
2505
+ Task {
2506
+ do {
2507
+ let response = try await backend. call ( with: request)
2508
+ do {
2509
+ // Try to parse the Firebase token response
2510
+ let authTokenResult = try AuthTokenResult . tokenResult ( token: response. firebaseToken)
2511
+ DispatchQueue . main. async {
2512
+ completion ( authTokenResult, nil )
2513
+ }
2514
+ } catch {
2515
+ // Failed to parse JWT
2516
+ DispatchQueue . main. async {
2517
+ completion ( nil , AuthErrorCode . malformedJWT)
2518
+ }
2519
+ }
2520
+ } catch {
2521
+ // Backend call failed
2522
+ DispatchQueue . main. async {
2523
+ completion ( nil , error)
2524
+ }
2525
+ }
2526
+ }
2527
+ }
2528
+
2529
+ /// Exchanges a third-party OIDC token for a Firebase STS token using Swift concurrency.
2530
+ ///
2531
+ /// This async variant performs the same operation as the completion-based method but returns
2532
+ /// the result directly and throws on failure.
2533
+ ///
2534
+ /// The `Auth` instance must be configured with `TenantConfig` containing `location` and `tenantId`.
2535
+ ///
2536
+ /// - Parameters:
2537
+ /// - idToken: The OIDC token received from the third-party Identity Provider (IdP).
2538
+ /// - idpConfigId: The identifier of the OIDC provider configuration defined in Firebase.
2539
+ /// - Returns: An `AuthTokenResult` containing the Firebase ID token and its expiration details.
2540
+ /// - Throws: An error if R-GCIP is not configured, if the network call fails,
2541
+ /// or if the token parsing fails.
2542
+ public func exchangeToken( idToken: String , idpConfigId: String ) async throws -> AuthTokenResult {
2543
+ // Ensure R-GCIP is configured with location and tenant ID
2544
+ guard let location = requestConfiguration. location,
2545
+ let tenantId = requestConfiguration. tenantId
2546
+ else {
2547
+ throw AuthErrorCode . operationNotAllowed
2548
+ }
2549
+
2550
+ // Build the exchange token request
2551
+ let request = ExchangeTokenRequest (
2552
+ idToken: idToken,
2553
+ idpConfigID: idpConfigId,
2554
+ config: requestConfiguration
2555
+ )
2556
+
2557
+ // Perform the backend call and return parsed token
2558
+ let response = try await backend. call ( with: request)
2559
+ return try AuthTokenResult . tokenResult ( token: response. firebaseToken)
2506
2560
}
2507
2561
}
0 commit comments