Protect your secrets!
Gate/AI is a secure authentication and API gateway client for mobile apps. It allows you to use secret key protected APIs without actually having the secret in your mobile app. It's an essential piece of security for protecting your secrets.
This is the supporting framework for iOS. It requires an account with Gate/AI to use. Find out more here: https://gate-ai.net
Access or setup your Gate here: https://portal.gate-ai.net
- β Secure Authentication: OAuth 2.0 + DPoP + App Attest flow handled automatically
- π Secure Enclave: Device keys stored in hardware-backed security
- π± App Attest: Leverages Apple's device attestation framework
- π Token Management: Automatic token refresh and caching
- π‘οΈ DPoP Proofing: Per-request proof-of-possession for enhanced security
- π Nonce Handling: Automatic retry for nonce challenges
- π οΈ Development Support: Simulator-friendly development token flow
- iOS 16.0+ / macOS 13.0+
- Xcode 16.0+
- Swift 6.0+
- Apple Developer account with App Attest entitlement added to your provisioning profile.
Add the following to your Package.swift file:
dependencies: [
.package(url: "https://github.com/LAND-MK-1/gate-ios.git", from: "1.0.0")
]Or in Xcode:
- File β Add Package Dependencies...
- Enter the repository URL
- Select the version or branch
- Add to your target
For local development, you can add the package directly:
- File β Add Package Dependencies...
- Click "Add Local..."
- Select the
gate-iosdirectory
import GateAI
// 1. Configure the client
let configuration = try GateAIConfiguration(
baseURLString: "https://yourteam.us01.gate-ai.net",
teamIdentifier: "ABCDE12345", // Your Apple Team ID
logLevel: .info
)
let client = GateAIClient(configuration: configuration)
// 2. Make authenticated requests
let requestBody = """
{
"model": "gpt-4",
"messages": [{"role": "user", "content": "Hello!"}]
}
""".data(using: .utf8)!
let (data, response) = try await client.performProxyRequest(
path: "openai/chat/completions",
method: .post,
body: requestBody,
additionalHeaders: ["Content-Type": "application/json"]
)
// 3. Process the response
if response.statusCode == 200 {
let result = try JSONDecoder().decode(ChatResponse.self, from: data)
print(result)
}let configuration = try GateAIConfiguration(
baseURLString: "https://yourteam.us01.gate-ai.net",
teamIdentifier: "ABCDE12345"
)#if targetEnvironment(simulator)
let devToken = ProcessInfo.processInfo.environment["GATE_AI_DEV_TOKEN"]
#else
let devToken: String? = nil
#endif
let configuration = try GateAIConfiguration(
baseURLString: "https://yourteam.us01.gate-ai.net",
teamIdentifier: "ABCDE12345",
developmentToken: devToken,
logLevel: .debug // Enable detailed logging
)let configuration = try GateAIConfiguration(
baseURLString: "https://yourteam.us01.gate-ai.net",
bundleIdentifier: "com.example.MyApp", // Override Bundle.main
teamIdentifier: "ABCDE12345"
)π Full Documentation
let (data, response) = try await client.performProxyRequest(
path: "openai/models",
method: .get
)struct ChatRequest: Codable {
let model: String
let messages: [Message]
}
let request = ChatRequest(model: "gpt-4", messages: [...])
let body = try JSONEncoder().encode(request)
let (data, response) = try await client.performProxyRequest(
path: "openai/chat/completions",
method: .post,
body: body,
additionalHeaders: ["Content-Type": "application/json"]
)do {
let (data, response) = try await client.performProxyRequest(...)
} catch let error as GateAIError {
switch error {
case .server(let statusCode, let serverError, _):
if serverError?.error == "rate_limited" {
print("Rate limited. Please try again later.")
}
case .network(let underlying):
print("Network error: \(underlying)")
default:
print("Error: \(error.localizedDescription)")
}
}In Xcode:
- Select your app target
- Go to Signing & Capabilities
- Add App Attest capability
In Apple Developer Portal (usually not needed):
- Go to your app identifier
- Enable App Attest capability
- Save and regenerate provisioning profiles
Add your Team ID and Bundle Identifier to your Gate.
- Go to the Gate/AI dashboard
- Edit your gate, enable iOS support and then add:
- Team ID (10 alphanumeric characters, e.g., "ABCDE12345")
- Bundle Identifier (com.acme.your-appp)
App Attest isn't supported in the simulator. To work around this, we provide a developer token to authenticate when running in the simulator.
- Go to the Gate/AI dashboard
- View your Gate
- Under Developer Tokens, clickon Create Dev Token
- Copy the token and add it to your
GateAIConfiguration. See next: Testing on Simulator
Note
Dev Tokens are short lived: either 1 week or 1 month. Follow the sample code and use #if to include only in simulator builds.
The SDK automatically uses development token flow on simulator:
#if targetEnvironment(simulator)
let devToken = "your-dev-token"
#else
let devToken: String? = nil
#endif
let configuration = try GateAIConfiguration(
baseURLString: "https://staging.us01.gate-ai.net", // Use staging
teamIdentifier: "ABCDE12345",
developmentToken: devToken
)Physical devices use the full App Attest flow. No development token is needed.
Enable logging to debug issues:
let configuration = try GateAIConfiguration(
baseURLString: "https://yourteam.us01.gate-ai.net",
teamIdentifier: "ABCDE12345",
logLevel: .debug // .off, .error, .warning, .info, .debug
)Logs appear in:
- Xcode console
- Console.app (filter by subsystem: "com.gate-ai.sdk")
- System logs
Sensitive headers (Authorization, DPoP, API keys) are automatically redacted.
Your team ID must be the 10-character Apple Team ID, not your team name. Find it in:
- Xcode β Project Settings β Signing & Capabilities
- Apple Developer Portal β Membership
- Use a physical iOS device (App Attest requires real hardware)
- Or configure a development token for simulator testing
- Verify your team ID is registered with your Gate.
- Check your bundle ID matches your Gate
- Try resetting:
try client.clearAppAttestKey(). This should not be necessary. Please create an issue if it was.
Ensure the URL string is valid and includes the protocol:
- β
https://yourteam.us01.gate-ai.net - β
yourteam.us01.gate-ai.net(missing protocol)
We hate using crappy frameworks and want good DX. If you have suggestions please let us know! Pull requests are welcome!
Found a bug or have a question? Open an issue with:
- Clear description of the problem
- Steps to reproduce
- Expected vs actual behavior
- Xcode version and iOS version
- Relevant code snippets or error messages
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Ensure all tests pass (
swift test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow existing code style and conventions
- Add DocC documentation for public APIs
- Include tests for new features
- Update README if adding new functionality
- Keep PRs focused on a single feature or fix
MIT License - see the LICENSE file for details.
This means you can freely use, modify, and distribute this SDK, including in commercial applications.
This is what we are doing behind the scenes to securely authenticate using DPoP and then send on your API service request.
