Skip to content

Commit e36a47a

Browse files
author
Guilherme Souza
authored
Add sign in with id token method (#49)
* Add sign in with id token method * Add test * ci: copy secrets file * Format and fix tests * Add deprecations * Fix initializer
1 parent b4aa56b commit e36a47a

File tree

11 files changed

+123
-78
lines changed

11 files changed

+123
-78
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ jobs:
3434
- uses: actions/checkout@v3
3535
- name: Select Xcode ${{ matrix.xcode }}
3636
run: sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode }}.app
37+
- name: Copy Secrets file
38+
run: cp Examples/Shared/Sources/_Secrets.swift Examples/Shared/Sources/Secrets.swift
3739
- name: Build example
3840
run: make build-example
3941

.swift-version

Lines changed: 0 additions & 1 deletion
This file was deleted.

.swiftformat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
--swiftversion 5.7
12
--binarygrouping none
23
--decimalgrouping none
34
--hexgrouping none

Examples/Shared/Sources/AppView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ struct AppView: View {
3838
}
3939
}
4040

41-
func stringfy<T: Codable>(_ value: T) -> String {
41+
func stringfy(_ value: some Codable) -> String {
4242
let encoder = JSONEncoder()
4343
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
4444
let data = try? encoder.encode(value)

Sources/GoTrue/Deprecated.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Foundation
2+
3+
extension GoTrueMetaSecurity {
4+
@available(*, deprecated, renamed: "captchaToken")
5+
public var hcaptchaToken: String {
6+
get { captchaToken }
7+
set { captchaToken = newValue }
8+
}
9+
10+
@available(*, deprecated, renamed: "init(captchaToken:)")
11+
public init(hcaptchaToken: String) {
12+
self.init(captchaToken: hcaptchaToken)
13+
}
14+
}

Sources/GoTrue/GoTrueClient.swift

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public final class GoTrueClient {
9797
email: email,
9898
password: password,
9999
data: data,
100-
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(hcaptchaToken:))
100+
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(captchaToken:))
101101
)
102102
)
103103
)
@@ -121,7 +121,7 @@ public final class GoTrueClient {
121121
password: password,
122122
phone: phone,
123123
data: data,
124-
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(hcaptchaToken:))
124+
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(captchaToken:))
125125
)
126126
)
127127
)
@@ -161,6 +161,18 @@ public final class GoTrueClient {
161161
)
162162
}
163163

164+
/// Allows signing in with an ID token issued by certain supported providers.
165+
/// The ID token is verified for validity and a new session is established.
166+
@discardableResult
167+
public func signInWithIdToken(credentials: OpenIDConnectCredentials) async throws -> Session {
168+
try await _signIn(
169+
request: Paths.token.post(
170+
grantType: .idToken,
171+
.openIDConnectCredentials(credentials)
172+
)
173+
)
174+
}
175+
164176
private func _signIn(request: Request<Session>) async throws -> Session {
165177
await Env.sessionManager.remove()
166178

@@ -200,7 +212,7 @@ public final class GoTrueClient {
200212
email: email,
201213
createUser: shouldCreateUser,
202214
data: data,
203-
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(hcaptchaToken:))
215+
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(captchaToken:))
204216
)
205217
)
206218
)
@@ -226,7 +238,7 @@ public final class GoTrueClient {
226238
phone: phone,
227239
createUser: shouldCreateUser,
228240
data: data,
229-
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(hcaptchaToken:))
241+
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(captchaToken:))
230242
)
231243
)
232244
)
@@ -251,11 +263,11 @@ public final class GoTrueClient {
251263
URLQueryItem(name: "provider", value: provider.rawValue),
252264
]
253265

254-
if let scopes = scopes {
266+
if let scopes {
255267
queryItems.append(URLQueryItem(name: "scopes", value: scopes))
256268
}
257269

258-
if let redirectTo = redirectTo {
270+
if let redirectTo {
259271
queryItems.append(URLQueryItem(name: "redirect_to", value: redirectTo.absoluteString))
260272
}
261273

@@ -383,7 +395,7 @@ public final class GoTrueClient {
383395
)
384396
}
385397

386-
guard let session = session else {
398+
guard let session else {
387399
throw GoTrueError.sessionNotFound
388400
}
389401

@@ -399,7 +411,7 @@ public final class GoTrueClient {
399411
let session = try? await Env.sessionManager.session()
400412
await Env.sessionManager.remove()
401413

402-
if let session = session {
414+
if let session {
403415
try await Env.client.send(Paths.logout.post.withAuthorization(session.accessToken)).value
404416
}
405417
}
@@ -420,7 +432,7 @@ public final class GoTrueClient {
420432
email: email,
421433
token: token,
422434
type: type,
423-
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(hcaptchaToken:))
435+
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(captchaToken:))
424436
)
425437
)
426438
)
@@ -440,7 +452,7 @@ public final class GoTrueClient {
440452
phone: phone,
441453
token: token,
442454
type: type,
443-
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(hcaptchaToken:))
455+
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(captchaToken:))
444456
)
445457
)
446458
)
@@ -483,7 +495,7 @@ public final class GoTrueClient {
483495
redirectTo: redirectTo,
484496
RecoverParams(
485497
email: email,
486-
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(hcaptchaToken:))
498+
gotrueMetaSecurity: captchaToken.map(GoTrueMetaSecurity.init(captchaToken:))
487499
)
488500
)
489501
).value

Sources/GoTrue/GoTrueLocalStorage.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct KeychainLocalStorage: GoTrueLocalStorage {
1111
let keychain: Keychain
1212

1313
init(service: String, accessGroup: String?) {
14-
if let accessGroup = accessGroup {
14+
if let accessGroup {
1515
keychain = Keychain(service: service, accessGroup: accessGroup)
1616
} else {
1717
keychain = Keychain(service: service)

Sources/GoTrue/Internal/SessionManager.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private actor LiveSessionManager {
3636
private var task: Task<Session, Error>?
3737

3838
func session() async throws -> Session {
39-
if let task = task {
39+
if let task {
4040
return try await task.value
4141
}
4242

Sources/GoTrue/Types.swift

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -301,44 +301,52 @@ public enum Provider: String, Codable, CaseIterable {
301301
}
302302

303303
public struct OpenIDConnectCredentials: Codable, Hashable {
304-
public var idToken: String
305-
public var nonce: String
306-
public var clientID: String?
307-
public var issuer: String?
304+
/// Only Apple and Google ID tokens are supported for use from within iOS or Android applications.
308305
public var provider: Provider?
309306

307+
/// ID token issued by Apple or Google.
308+
public var token: String
309+
310+
/// If the ID token contains a `nonce`, then the hash of this value is compared to the value in
311+
/// the ID token.
312+
public var nonce: String?
313+
314+
/// Verification token received when the user completes the captcha on the site.
315+
public var gotrueMetaSecurity: GoTrueMetaSecurity?
316+
310317
public init(
311-
idToken: String,
312-
nonce: String,
313-
clientID: String? = nil,
314-
issuer: String? = nil,
315-
provider: Provider? = nil
318+
provider: Provider? = nil,
319+
token: String,
320+
nonce: String? = nil,
321+
gotrueMetaSecurity: GoTrueMetaSecurity? = nil
316322
) {
317-
self.idToken = idToken
318-
self.nonce = nonce
319-
self.clientID = clientID
320-
self.issuer = issuer
321323
self.provider = provider
324+
self.token = token
325+
self.nonce = nonce
326+
self.gotrueMetaSecurity = gotrueMetaSecurity
322327
}
323328

324329
public enum CodingKeys: String, CodingKey {
325-
case idToken = "id_token"
326-
case nonce
327-
case clientID = "client_id"
328-
case issuer
329330
case provider
331+
case token
332+
case nonce
333+
case gotrueMetaSecurity = "gotrue_meta_security"
334+
}
335+
336+
public enum Provider: String, Codable, Hashable {
337+
case google, apple
330338
}
331339
}
332340

333341
public struct GoTrueMetaSecurity: Codable, Hashable {
334-
public var hcaptchaToken: String
342+
public var captchaToken: String
335343

336-
public init(hcaptchaToken: String) {
337-
self.hcaptchaToken = hcaptchaToken
344+
public init(captchaToken: String) {
345+
self.captchaToken = captchaToken
338346
}
339347

340348
public enum CodingKeys: String, CodingKey {
341-
case hcaptchaToken = "hcaptcha_token"
349+
case captchaToken = "captcha_token"
342350
}
343351
}
344352

Tests/GoTrueTests/GoTrueTests.swift

Lines changed: 14 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,32 @@ final class GoTrueTests: XCTestCase {
4040
func testDecodeSessionOrUser() {
4141
XCTAssertNoThrow(
4242
try JSONDecoder.goTrue.decode(
43-
AuthResponse.self, from: sessionJSON.data(using: .utf8)!
43+
AuthResponse.self, from: json(named: "session")
4444
)
4545
)
4646
}
4747

4848
#if !os(watchOS)
49-
// Not working on watchOS, don't know why.
5049
func test_signUpWithEmailAndPassword() async throws {
5150
Mock.post(path: "signup", json: "signup-response").register()
5251

5352
let user = try await sut.signUp(email: "guilherme@grds.dev", password: "thepass").user
5453

5554
XCTAssertEqual(user?.email, "guilherme@grds.dev")
5655
}
56+
57+
func testSignInWithIdToken() async throws {
58+
Mock(
59+
url: URL(string: "http://localhost:54321/auth/v1/token?grant_type=id_token")!,
60+
dataType: .json,
61+
statusCode: 200,
62+
data: [.post: json(named: "session")]
63+
).register()
64+
65+
let session = try await sut
66+
.signInWithIdToken(credentials: OpenIDConnectCredentials(token: "dummy-token-1234"))
67+
XCTAssertEqual(session.user.email, "guilherme@binaryscraping.co")
68+
}
5769
#endif
5870

5971
func testSignInWithProvider() throws {
@@ -110,43 +122,3 @@ final class GoTrueTests: XCTestCase {
110122
}
111123
}
112124
}
113-
114-
let sessionJSON = """
115-
{
116-
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjQ4NjQwMDIxLCJzdWIiOiJmMzNkM2VjOS1hMmVlLTQ3YzQtODBlMS01YmQ5MTlmM2Q4YjgiLCJlbWFpbCI6Imd1aWxoZXJtZTJAZ3Jkcy5kZXYiLCJwaG9uZSI6IiIsImFwcF9tZXRhZGF0YSI6eyJwcm92aWRlciI6ImVtYWlsIiwicHJvdmlkZXJzIjpbImVtYWlsIl19LCJ1c2VyX21ldGFkYXRhIjp7fSwicm9sZSI6ImF1dGhlbnRpY2F0ZWQifQ.4lMvmz2pJkWu1hMsBgXP98Fwz4rbvFYl4VA9joRv6kY",
117-
"token_type": "bearer",
118-
"expires_in": 3600,
119-
"refresh_token": "GGduTeu95GraIXQ56jppkw",
120-
"user": {
121-
"id": "f33d3ec9-a2ee-47c4-80e1-5bd919f3d8b8",
122-
"aud": "authenticated",
123-
"role": "authenticated",
124-
"email": "guilherme2@grds.dev",
125-
"email_confirmed_at": "2022-03-30T10:33:41.018575157Z",
126-
"phone": "",
127-
"last_sign_in_at": "2022-03-30T10:33:41.021531328Z",
128-
"app_metadata": {
129-
"provider": "email",
130-
"providers": [
131-
"email"
132-
]
133-
},
134-
"user_metadata": {},
135-
"identities": [
136-
{
137-
"id": "f33d3ec9-a2ee-47c4-80e1-5bd919f3d8b8",
138-
"user_id": "f33d3ec9-a2ee-47c4-80e1-5bd919f3d8b8",
139-
"identity_data": {
140-
"sub": "f33d3ec9-a2ee-47c4-80e1-5bd919f3d8b8"
141-
},
142-
"provider": "email",
143-
"last_sign_in_at": "2022-03-30T10:33:41.015557063Z",
144-
"created_at": "2022-03-30T10:33:41.015612Z",
145-
"updated_at": "2022-03-30T10:33:41.015616Z"
146-
}
147-
],
148-
"created_at": "2022-03-30T10:33:41.005433Z",
149-
"updated_at": "2022-03-30T10:33:41.022688Z"
150-
}
151-
}
152-
"""

0 commit comments

Comments
 (0)