Skip to content

Commit 30472ce

Browse files
committed
fix: properly using nonce for SIWA
1 parent 56cd3c4 commit 30472ce

File tree

5 files changed

+63
-10
lines changed

5 files changed

+63
-10
lines changed

Sources/CompilerSwiftAI/Auth/CompilerCient+AppleAuth.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import AuthenticationServices
44

55
extension CompilerClient {
6-
public func handleSignInWithApple(_ result: Result<ASAuthorization, Error>) async throws -> Bool {
6+
public func handleSignInWithApple(_ result: Result<ASAuthorization, Error>, nonce: String?) async throws -> Bool {
77
switch result {
88
case .success(let auth):
99
guard let appleIDCredential = auth.credential as? ASAuthorizationAppleIDCredential,
@@ -12,11 +12,15 @@ extension CompilerClient {
1212
throw AuthError.invalidToken
1313
}
1414

15-
// Store Apple ID token - this acts as our "refresh token"
16-
// Apple ID tokens can be reused for a while (usually days to weeks)
15+
// Store Apple ID token
1716
await keychain.save(idToken, service: "apple-id-token", account: "user")
1817

19-
let accessToken = try await authenticateWithServer(idToken: idToken)
18+
// Store the nonce for verification
19+
if let nonce = nonce {
20+
await keychain.save(nonce, service: "apple-nonce", account: "user")
21+
}
22+
23+
let accessToken = try await authenticateWithServer(idToken: idToken, nonce: nonce)
2024
await keychain.save(accessToken, service: "access-token", account: "user")
2125

2226
return true

Sources/CompilerSwiftAI/Auth/CompilerClient+Auth.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,23 @@ extension CompilerClient {
2828
throw AuthError.invalidToken
2929
}
3030

31-
func authenticateWithServer(idToken: String) async throws -> String {
31+
func authenticateWithServer(idToken: String, nonce: String? = nil) async throws -> String {
3232
let lowercasedAppID = appID.uuidString.lowercased()
3333
let endpoint = "\(baseURL)/v1/apps/\(lowercasedAppID)/end-users/apple"
3434
guard let url = URL(string: endpoint) else {
3535
authLogger.error("Invalid URL: \(self.baseURL)")
3636
throw AuthError.invalidResponse
3737
}
3838

39+
var body: [String: String] = ["id_token": idToken]
40+
if let nonce {
41+
body["nonce"] = nonce
42+
}
3943
authLogger.debug("Making auth request to: \(endpoint)")
4044

4145
var request = URLRequest(url: url)
4246
request.httpMethod = "POST"
4347
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
44-
45-
let body = ["id_token": idToken]
4648
request.httpBody = try JSONEncoder().encode(body)
4749

4850
authLogger.debug("Request body: \(body)")
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import CryptoKit
2+
import AuthenticationServices
3+
4+
extension CompilerClient {
5+
// Generate a random nonce for authentication
6+
public static func randomNonceString(length: Int = 32) -> String {
7+
precondition(length > 0)
8+
let charset: [Character] = Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
9+
var result = ""
10+
var remainingLength = length
11+
12+
while remainingLength > 0 {
13+
let randoms: [UInt8] = (0 ..< 16).map { _ in
14+
var random: UInt8 = 0
15+
let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
16+
if errorCode != errSecSuccess {
17+
fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)")
18+
}
19+
return random
20+
}
21+
22+
randoms.forEach { random in
23+
if remainingLength == 0 {
24+
return
25+
}
26+
27+
if random < charset.count {
28+
result.append(charset[Int(random)])
29+
remainingLength -= 1
30+
}
31+
}
32+
}
33+
34+
return result
35+
}
36+
37+
// Compute the SHA256 hash of a string
38+
public static func sha256(_ input: String) -> String {
39+
let inputData = Data(input.utf8)
40+
let hashedData = SHA256.hash(data: inputData)
41+
let hashString = hashedData.compactMap {
42+
String(format: "%02x", $0)
43+
}.joined()
44+
45+
return hashString
46+
}
47+
}

Sources/CompilerSwiftAI/CompilerClient.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ public final actor CompilerClient {
2828

2929
private(set) var configuration: Configuration
3030

31-
internal let baseURL: String = "https://backend.compiler.inc"
32-
// internal let baseURL: String = "http://localhost:3000"
31+
// internal let baseURL: String = "https://backend.compiler.inc"
32+
internal let baseURL: String = "http://localhost:3000"
3333
internal let keychain: KeychainHelper = KeychainHelper.standard
3434
internal let functionLogger: DebugLogger
3535
internal let modelLogger: DebugLogger

Sources/CompilerSwiftAI/Model Calling/CompilerClient+Streaming.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ struct ChatResponseDataTransferObject: Decodable {
77
}
88

99
extension CompilerClient {
10-
var streamingProviders: [ModelProvider] { [.openai, .anthropic, .gemini] }
10+
var streamingProviders: [ModelProvider] { [.openai, .anthropic, .google] }
1111

1212
// Specialized String streaming version
1313
func makeStreamingModelCall(

0 commit comments

Comments
 (0)