Skip to content

Exchange token tests #14984

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: exchangeToken-auth
Choose a base branch
from
154 changes: 154 additions & 0 deletions ExchangeTokenRequestTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation
import XCTest

@testable import FirebaseAuth
import FirebaseCore

/// Tests for `ExchangeTokenRequest`
@available(iOS 13, *)
class ExchangeTokenRequestTests: XCTestCase {
// MARK: - Constants for Testing

let kAPIKey = "test-api-key"
let kProjectID = "test-project-id"
let kLocation = "us-east1"
let kTenantID = "test-tenant-id-123"
let kIdToken = "a-very-long-and-secure-oidc-token-string"
let kIdpConfigId = "oidc.my-test-provider"

let kProductionHost = "identityplatform.googleapis.com"
let kStagingHost = "staging-identityplatform.sandbox.googleapis.com"

// MARK: - Test Cases

/// Tests that the production URL is correctly formed for a specific region.
func testProductionURLIsCorrectlyConstructed() {
let (auth, app) = createTestAuthInstance(
projectID: kProjectID,
location: kLocation,
tenantId: kTenantID
)

let request = ExchangeTokenRequest(
idToken: kIdToken,
idpConfigID: kIdpConfigId,
config: auth.requestConfiguration,
useStaging: false
)

let expectedHost = "\(kLocation)-\(kProductionHost)"
let expectedURL = "https://\(expectedHost)/v2beta/projects/\(kProjectID)" +
"/locations/\(kLocation)/tenants/\(kTenantID)/idpConfigs/\(kIdpConfigId):exchangeOidcToken?key=\(kAPIKey)"

XCTAssertEqual(request.requestURL().absoluteString, expectedURL)
}

/// Tests that the production URL is correctly formed for the "prod-global" location.
func testProductionURLIsCorrectlyConstructedForGlobalLocation() {
let (auth, app) = createTestAuthInstance(
projectID: kProjectID,
location: "prod-global",
tenantId: kTenantID
)
_ = app

let request = ExchangeTokenRequest(
idToken: kIdToken,
idpConfigID: kIdpConfigId,
config: auth.requestConfiguration,
useStaging: false
)

let expectedHost = kProductionHost
let expectedURL = "https://\(expectedHost)/v2beta/projects/\(kProjectID)" +
"/locations/global/tenants/\(kTenantID)/idpConfigs/\(kIdpConfigId):exchangeOidcToken?key=\(kAPIKey)"

XCTAssertEqual(request.requestURL().absoluteString, expectedURL)
}

/// Tests that the staging URL is correctly formed.
func testStagingURLIsCorrectlyConstructed() {
let (auth, app) = createTestAuthInstance(
projectID: kProjectID,
location: kLocation,
tenantId: kTenantID
)
_ = app

let request = ExchangeTokenRequest(
idToken: kIdToken,
idpConfigID: kIdpConfigId,
config: auth.requestConfiguration,
useStaging: true
)

let expectedHost = "\(kLocation)-\(kStagingHost)"
let expectedURL = "https://\(expectedHost)/v2beta/projects/\(kProjectID)" +
"/locations/\(kLocation)/tenants/\(kTenantID)/idpConfigs/\(kIdpConfigId):exchangeOidcToken?key=\(kAPIKey)"

XCTAssertEqual(request.requestURL().absoluteString, expectedURL)
}

/// Tests that the unencoded HTTP body contains the correct id_token.
func testUnencodedHTTPBodyIsCorrect() {
let (auth, app) = createTestAuthInstance(
projectID: kProjectID,
location: kLocation,
tenantId: kTenantID
)
_ = app

let request = ExchangeTokenRequest(
idToken: kIdToken,
idpConfigID: kIdpConfigId,
config: auth.requestConfiguration
)

let body = request.unencodedHTTPRequestBody
XCTAssertNotNil(body)
XCTAssertEqual(body?.count, 1)
XCTAssertEqual(body?["id_token"] as? String, kIdToken)
}

// MARK: - Helper Function

/// Creates a test FirebaseApp and Auth instance with specified configurations.
private func createTestAuthInstance(projectID: String?, location: String?,
tenantId: String?) -> (auth: Auth, app: FirebaseApp) {
let appName = "TestApp-\(UUID().uuidString)"
let options = FirebaseOptions(
googleAppID: "1:1234567890:ios:abcdef123456",
gcmSenderID: "1234567890"
)
options.apiKey = kAPIKey
if let projectID = projectID {
options.projectID = projectID
}

if FirebaseApp.app(name: appName) != nil {
FirebaseApp.app(name: appName)?.delete { _ in }
}
let app = FirebaseApp(instanceWithName: appName, options: options)

let auth = Auth(app: app)
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we not do let auth = Auth(app: app, tenantConfig: tenantConfig) ? Why are we setting these 2 separately? This is different from how we implemented in other client SDKs.

auth.app = app
auth.requestConfiguration.location = location
auth.requestConfiguration.tenantId = tenantId

return (auth, app)
}
}
213 changes: 213 additions & 0 deletions FirebaseAuth/Tests/Unit/ExchangeTokenRequestTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import Foundation
import XCTest

@testable import FirebaseAuth
import FirebaseCore

/// Tests for `ExchangeTokenRequest`
@available(iOS 13, *)
class ExchangeTokenRequestTests: XCTestCase {
// MARK: - Constants for Testing

let kAPIKey = "test-api-key"
let kProjectID = "test-project-id"
let kLocation = "us-east1"
let kTenantID = "test-tenant-id-123"
let kIdToken = "a-very-long-and-secure-oidc-token-string"
let kIdpConfigId = "oidc.my-test-provider"

// These should match the constants in ExchangeTokenRequest.swift
let kProductionHost = "identityplatform.googleapis.com"
let kStagingHost = "staging-identityplatform.sandbox.googleapis.com"

// MARK: - Helper Function

/// Creates a test FirebaseApp and Auth instance with specified configurations.
private func createTestAuthInstance(projectID: String?, location: String?,
tenantId: String?) -> (auth: Auth, app: FirebaseApp) {
let appName = "TestApp-\(UUID().uuidString)"
let options = FirebaseOptions(
googleAppID: "1:1234567890:ios:abcdef123456",
gcmSenderID: "1234567890"
)
options.apiKey = kAPIKey
if let projectID = projectID {
options.projectID = projectID
}

if let existingApp = FirebaseApp.app(name: appName) {
existingApp.delete { _ in }
}
let app = FirebaseApp(instanceWithName: appName, options: options)

let auth: Auth
if let loc = location, let tid = tenantId {
let tenantConfig = Auth.TenantConfig(tenantId: tid, location: loc)
auth = Auth(app: app, tenantConfig: tenantConfig)
} else {
// This case should not be hit in these tests as all tests provide location and tenantId
auth = Auth(app: app)
}

return (auth, app)
}

/// Helper to add debugging assertions.
private func checkPreconditions(auth: Auth, app: FirebaseApp, expectedLocation: String,
expectedTenantId: String, expectedProjectId: String) {
XCTAssertNotNil(auth.requestConfiguration.tenantConfig, "tenantConfig should not be nil")
XCTAssertEqual(
auth.requestConfiguration.tenantConfig?.location,
expectedLocation,
"Location should match"
)
XCTAssertEqual(
auth.requestConfiguration.tenantConfig?.tenantId,
expectedTenantId,
"Tenant ID should match"
)

XCTAssertNotNil(auth.requestConfiguration.auth, "config.auth should not be nil")
XCTAssertTrue(
auth.requestConfiguration.auth === auth,
"config.auth should be the same instance"
)

XCTAssertNotNil(auth.app, "Auth.app should not be nil")
XCTAssertTrue(auth.app === app, "Auth.app should be the same instance")

XCTAssertNotNil(auth.app?.options, "App options should not be nil")
XCTAssertEqual(auth.app?.options.projectID, expectedProjectId, "Project ID should match")
}

// MARK: - Test Cases

/// Tests that the production URL is correctly formed for a specific region.
func testProductionURLIsCorrectlyConstructed() {
let (auth, app) = createTestAuthInstance(
projectID: kProjectID,
location: kLocation,
tenantId: kTenantID
)
checkPreconditions(
auth: auth,
app: app,
expectedLocation: kLocation,
expectedTenantId: kTenantID,
expectedProjectId: kProjectID
)

let request = ExchangeTokenRequest(
idToken: kIdToken,
idpConfigID: kIdpConfigId,
config: auth.requestConfiguration,
useStaging: false
)

let expectedHost = "\(kLocation)-\(kProductionHost)"
let expectedURL = "https://\(expectedHost)/v2beta/projects/\(kProjectID)" +
"/locations/\(kLocation)/tenants/\(kTenantID)/idpConfigs/\(kIdpConfigId):exchangeOidcToken?key=\(kAPIKey)"

XCTAssertEqual(request.requestURL().absoluteString, expectedURL)
}

/// Tests that the production URL is correctly formed for the "global" location.
func testProductionURLIsCorrectlyConstructedForGlobalLocation() {
let globalLocation = "global"
let (auth, app) = createTestAuthInstance(
projectID: kProjectID,
location: globalLocation,
tenantId: kTenantID
)
checkPreconditions(
auth: auth,
app: app,
expectedLocation: globalLocation,
expectedTenantId: kTenantID,
expectedProjectId: kProjectID
)

let request = ExchangeTokenRequest(
idToken: kIdToken,
idpConfigID: kIdpConfigId,
config: auth.requestConfiguration,
useStaging: false
)

let expectedHost = kProductionHost
let expectedURL = "https://\(expectedHost)/v2beta/projects/\(kProjectID)" +
"/locations/global/tenants/\(kTenantID)/idpConfigs/\(kIdpConfigId):exchangeOidcToken?key=\(kAPIKey)"

XCTAssertEqual(request.requestURL().absoluteString, expectedURL)
}

/// Tests that the staging URL is correctly formed.
func testStagingURLIsCorrectlyConstructed() {
let (auth, app) = createTestAuthInstance(
projectID: kProjectID,
location: kLocation,
tenantId: kTenantID
)
checkPreconditions(
auth: auth,
app: app,
expectedLocation: kLocation,
expectedTenantId: kTenantID,
expectedProjectId: kProjectID
)

let request = ExchangeTokenRequest(
idToken: kIdToken,
idpConfigID: kIdpConfigId,
config: auth.requestConfiguration,
useStaging: true
)

let expectedHost = "\(kLocation)-\(kStagingHost)"
let expectedURL = "https://\(expectedHost)/v2beta/projects/\(kProjectID)" +
"/locations/\(kLocation)/tenants/\(kTenantID)/idpConfigs/\(kIdpConfigId):exchangeOidcToken?key=\(kAPIKey)"

XCTAssertEqual(request.requestURL().absoluteString, expectedURL)
}

/// Tests that the unencoded HTTP body contains the correct id_token.
func testUnencodedHTTPBodyIsCorrect() {
let (auth, app) = createTestAuthInstance(
projectID: kProjectID,
location: kLocation,
tenantId: kTenantID
)
checkPreconditions(
auth: auth,
app: app,
expectedLocation: kLocation,
expectedTenantId: kTenantID,
expectedProjectId: kProjectID
)

let request = ExchangeTokenRequest(
idToken: kIdToken,
idpConfigID: kIdpConfigId,
config: auth.requestConfiguration
)

let body = request.unencodedHTTPRequestBody
XCTAssertNotNil(body)
XCTAssertEqual(body?.count, 1)
XCTAssertEqual(body?["id_token"] as? String, kIdToken)
}
}
Loading