Skip to content
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions FirebaseAuth/Sources/Swift/Auth/Auth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2463,4 +2463,60 @@ public extension Auth {
self.tenantId = tenantId
}
}

/// Represents the result of a successful OIDC token exchange, containing a Firebase ID token
/// and its expiration.
struct FirebaseToken: Sendable {
/// The Firebase ID token string.
public let token: String
/// The date at which the Firebase ID token expires.
public let expirationDate: Date

init(token: String, expirationDate: Date) {
self.token = token
self.expirationDate = expirationDate
}
}

/// Exchanges a third-party OIDC ID token for a Firebase ID token.
///
/// This method is used for Bring Your Own CIAM (BYO-CIAM) in Regionalized GCIP (R-GCIP),
/// where the `Auth` instance must be configured with a `TenantConfig`, including `location`
/// and `tenantId`, typically by using `Auth.auth(app:tenantConfig:)`.
///
/// Unlike standard sign-in methods, this flow *does not* create or update a `User`object and
/// *does not* set `CurrentUser` on the `Auth` instance. It only returns a Firebase token.
///
/// - Parameters:
/// - oidcToken: The OIDC ID token obtained from the third-party identity provider.
/// - idpConfigId: The ID of the Identity Provider configuration within your GCIP tenant
/// - useStaging: A Boolean value indicating whether to use the staging Identity Platform
/// backend. Defaults to `false`.
/// - Returns: A `FirebaseToken` containing the Firebase ID token and its expiration date.
/// - Throws: An error if the `Auth` instance is not configured for R-GCIP, if the network
/// call fails, or if the token response parsing fails.
func exchangeToken(idToken: String, idpConfigId: String,
useStaging: Bool = false) async throws -> FirebaseToken {
// Ensure R-GCIP is configured with location and tenant ID
guard let _ = requestConfiguration.tenantConfig?.location,
let _ = requestConfiguration.tenantConfig?.tenantId
else {
throw AuthErrorUtils.operationNotAllowedError(message: "R-GCIP is not configured.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this should instead cause a fatal error? When do we anticipate this case triggering, during development or possibly in live app's execution?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be during development. Unless the developer pushes their app without testing :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I think it should be a fatal error then. That way it is very apparent that the developer's integration is incorrect.

}
let request = ExchangeTokenRequest(
idToken: idToken,
idpConfigID: idpConfigId,
config: requestConfiguration,
useStaging: useStaging
)
do {
let response = try await backend.call(with: request)
return FirebaseToken(
token: response.firebaseToken,
expirationDate: response.expirationDate
)
} catch {
throw error
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Represents the result of a successful OIDC token exchange, containing a Firebase ID token
/// and its expiration.
struct FirebaseToken: Sendable {
/// The Firebase ID token string.
public let token: String
/// The date at which the Firebase ID token expires.
public let expirationDate: Date
init(token: String, expirationDate: Date) {
self.token = token
self.expirationDate = expirationDate
}
}
/// Exchanges a third-party OIDC ID token for a Firebase ID token.
///
/// This method is used for Bring Your Own CIAM (BYO-CIAM) in Regionalized GCIP (R-GCIP),
/// where the `Auth` instance must be configured with a `TenantConfig`, including `location`
/// and `tenantId`, typically by using `Auth.auth(app:tenantConfig:)`.
///
/// Unlike standard sign-in methods, this flow *does not* create or update a `User`object and
/// *does not* set `CurrentUser` on the `Auth` instance. It only returns a Firebase token.
///
/// - Parameters:
/// - oidcToken: The OIDC ID token obtained from the third-party identity provider.
/// - idpConfigId: The ID of the Identity Provider configuration within your GCIP tenant
/// - useStaging: A Boolean value indicating whether to use the staging Identity Platform
/// backend. Defaults to `false`.
/// - Returns: A `FirebaseToken` containing the Firebase ID token and its expiration date.
/// - Throws: An error if the `Auth` instance is not configured for R-GCIP, if the network
/// call fails, or if the token response parsing fails.
func exchangeToken(idToken: String, idpConfigId: String,
useStaging: Bool = false) async throws -> FirebaseToken {
// Ensure R-GCIP is configured with location and tenant ID
guard let _ = requestConfiguration.tenantConfig?.location,
let _ = requestConfiguration.tenantConfig?.tenantId
else {
throw AuthErrorUtils.operationNotAllowedError(message: "R-GCIP is not configured.")
}
let request = ExchangeTokenRequest(
idToken: idToken,
idpConfigID: idpConfigId,
config: requestConfiguration,
useStaging: useStaging
)
do {
let response = try await backend.call(with: request)
return FirebaseToken(
token: response.firebaseToken,
expirationDate: response.expirationDate
)
} catch {
throw error
}
}
}
/// Exchanges a third-party OIDC ID token for a Firebase ID token.
///
/// This method is used for Bring Your Own CIAM (BYO-CIAM) in Regionalized GCIP (R-GCIP),
/// where the `Auth` instance must be configured with a `TenantConfig`, including `location`
/// and `tenantId`, typically by using `Auth.auth(app:tenantConfig:)`.
///
/// Unlike standard sign-in methods, this flow *does not* create or update a `User`object and
/// *does not* set `CurrentUser` on the `Auth` instance. It only returns a Firebase token.
///
/// - Parameters:
/// - oidcToken: The OIDC ID token obtained from the third-party identity provider.
/// - idpConfigId: The ID of the Identity Provider configuration within your GCIP tenant
/// - useStaging: A Boolean value indicating whether to use the staging Identity Platform
/// backend. Defaults to `false`.
/// - Returns: A `FirebaseToken` containing the Firebase ID token and its expiration date.
/// - Throws: An error if the `Auth` instance is not configured for R-GCIP, if the network
/// call fails, or if the token response parsing fails.
func exchangeToken(idToken: String, idpConfigId: String,
useStaging: Bool = false) async throws -> FirebaseToken {
// Ensure R-GCIP is configured with location and tenant ID
guard let _ = requestConfiguration.tenantConfig?.location,
let _ = requestConfiguration.tenantConfig?.tenantId
else {
throw AuthErrorUtils.operationNotAllowedError(message: "R-GCIP is not configured.")
}
let request = ExchangeTokenRequest(
idToken: idToken,
idpConfigID: idpConfigId,
config: requestConfiguration,
useStaging: useStaging
)
do {
let response = try await backend.call(with: request)
return FirebaseToken(
token: response.firebaseToken,
expirationDate: response.expirationDate
)
} catch {
throw error
}
}
}
/// Represents the result of a successful OIDC token exchange, containing a Firebase ID token
/// and its expiration.
public struct FirebaseToken: Sendable {
/// The Firebase ID token string.
public let token: String
/// The date at which the Firebase ID token expires.
public let expirationDate: Date
init(token: String, expirationDate: Date) {
self.token = token
self.expirationDate = expirationDate
}
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also doesn't look quite right. The FirebaseToken should be moved outside of the Auth {} scope. Left a suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes Nick, thanks for pointing this out. updated in latest commit.

Loading