File tree 5 files changed +94
-11
lines changed
Supabase.xcworkspace/xcshareddata/swiftpm
5 files changed +94
-11
lines changed Original file line number Diff line number Diff line change @@ -28,16 +28,16 @@ func extractParams(from url: URL) -> [String: String] {
28
28
private func extractParams( from fragment: String ) -> [ URLQueryItem ] {
29
29
let components =
30
30
fragment
31
- . split ( separator: " & " )
32
- . map { $0. split ( separator: " = " ) }
31
+ . split ( separator: " & " )
32
+ . map { $0. split ( separator: " = " ) }
33
33
34
34
return
35
35
components
36
- . compactMap {
37
- $0. count == 2
38
- ? URLQueryItem ( name: String ( $0 [ 0 ] ) , value: String ( $0 [ 1 ] ) )
39
- : nil
40
- }
36
+ . compactMap {
37
+ $0. count == 2
38
+ ? URLQueryItem ( name: String ( $0 [ 0 ] ) , value: String ( $0 [ 1 ] ) )
39
+ : nil
40
+ }
41
41
}
42
42
43
43
func decode( jwt: String ) throws -> [ String : Any ] ? {
Original file line number Diff line number Diff line change
1
+ import Foundation
2
+ import HTTPTypes
3
+ import IssueReporting
4
+
5
+ let base64UrlRegex = try ! NSRegularExpression (
6
+ pattern: " ^([a-z0-9_-]{4})*($|[a-z0-9_-]{3}$|[a-z0-9_-]{2}$) " , options: . caseInsensitive)
7
+
8
+ /// Checks that the value somewhat looks like a JWT, does not do any additional parsing or verification.
9
+ func isJWT( _ value: String ) -> Bool {
10
+ var token = value
11
+
12
+ if token. hasPrefix ( " Bearer " ) {
13
+ token = String ( token. dropFirst ( " Bearer " . count) )
14
+ }
15
+
16
+ token = token. trimmingCharacters ( in: . whitespacesAndNewlines)
17
+
18
+ guard !token. isEmpty else {
19
+ return false
20
+ }
21
+
22
+ let parts = token. split ( separator: " . " )
23
+
24
+ guard parts. count == 3 else {
25
+ return false
26
+ }
27
+
28
+ for part in parts {
29
+ if part. count < 4 || !isBase64Url( String ( part) ) {
30
+ return false
31
+ }
32
+ }
33
+
34
+ return true
35
+ }
36
+
37
+ func isBase64Url( _ value: String ) -> Bool {
38
+ let range = NSRange ( location: 0 , length: value. utf16. count)
39
+ return base64UrlRegex. firstMatch ( in: value, options: [ ] , range: range) != nil
40
+ }
41
+
42
+ func checkAuthorizationHeader(
43
+ _ headers: HTTPFields ,
44
+ fileID: StaticString = #fileID,
45
+ filePath: StaticString = #filePath,
46
+ line: UInt = #line,
47
+ column: UInt = #column
48
+ ) {
49
+ guard let authorization = headers [ . authorization] else { return }
50
+
51
+ if !isJWT( authorization) {
52
+ reportIssue (
53
+ " Authorization header does not contain a JWT " ,
54
+ fileID: fileID,
55
+ filePath: filePath,
56
+ line: line,
57
+ column: column
58
+ )
59
+ }
60
+ }
Original file line number Diff line number Diff line change @@ -161,6 +161,8 @@ public final class SupabaseClient: Sendable {
161
161
] )
162
162
. merging ( with: HTTPFields ( options. global. headers) )
163
163
164
+ checkAuthorizationHeader ( _headers)
165
+
164
166
// default storage key uses the supabase project ref as a namespace
165
167
let defaultStorageKey = " sb- \( supabaseURL. host!. split ( separator: " . " ) [ 0 ] ) -auth-token "
166
168
@@ -351,14 +353,18 @@ public final class SupabaseClient: Sendable {
351
353
}
352
354
353
355
private func adapt( request: URLRequest ) async -> URLRequest {
356
+ let defaultAccessToken = isJWT ( supabaseKey) ? supabaseKey : nil
357
+
354
358
let token: String ? = if let accessToken = options. auth. accessToken {
355
359
try ? await accessToken ( )
360
+ } else if let accessToken = try ? await auth. session. accessToken {
361
+ accessToken
356
362
} else {
357
- try ? await auth . session . accessToken
363
+ defaultAccessToken
358
364
}
359
365
360
366
var request = request
361
- if let token {
367
+ if let token, isJWT ( token ) , request . value ( forHTTPHeaderField : " Authorization " ) == nil {
362
368
request. setValue ( " Bearer \( token) " , forHTTPHeaderField: " Authorization " )
363
369
}
364
370
return request
Original file line number Diff line number Diff line change 68
68
"kind" : " remoteSourceControl" ,
69
69
"location" : " https://github.com/pointfreeco/swift-concurrency-extras" ,
70
70
"state" : {
71
- "revision" : " bb5059bde9022d69ac516803f4f227d8ac967f71 " ,
72
- "version" : " 1.1 .0"
71
+ "revision" : " 6054df64b55186f08b6d0fd87152081b8ad8d613 " ,
72
+ "version" : " 1.2 .0"
73
73
}
74
74
},
75
75
{
Original file line number Diff line number Diff line change
1
+ @testable import Supabase
2
+ import XCTest
3
+
4
+ final class HeleperTests : XCTestCase {
5
+ func testIsJWT( ) {
6
+ XCTAssertTrue ( isJWT ( " eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c " ) )
7
+ XCTAssertTrue ( isJWT ( " Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c " ) )
8
+ XCTAssertFalse ( isJWT ( " invalid.token.format " ) )
9
+ XCTAssertFalse ( isJWT ( " part1.part2.part3.part4 " ) )
10
+ XCTAssertFalse ( isJWT ( " part1.part2 " ) )
11
+ XCTAssertFalse ( isJWT ( " .. " ) )
12
+ XCTAssertFalse ( isJWT ( " a.a.a " ) )
13
+ XCTAssertFalse ( isJWT ( " eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.*&@!.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c " ) )
14
+ XCTAssertFalse ( isJWT ( " " ) )
15
+ XCTAssertFalse ( isJWT ( " Bearer " ) )
16
+ }
17
+ }
You can’t perform that action at this time.
0 commit comments