File tree Expand file tree Collapse file tree 4 files changed +87
-8
lines changed Expand file tree Collapse file tree 4 files changed +87
-8
lines changed Original file line number Diff line number Diff line change @@ -28,14 +28,14 @@ func extractParams(from url: URL) -> [String: String] {
2828private func extractParams( from fragment: String ) -> [ URLQueryItem ] {
2929 let components =
3030 fragment
31- . split ( separator: " & " )
32- . map { $0. split ( separator: " = " ) }
31+ . split ( separator: " & " )
32+ . map { $0. split ( separator: " = " ) }
3333
3434 return
3535 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+ }
4141}
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 @@ -170,6 +170,8 @@ public final class SupabaseClient: Sendable {
170170 ] )
171171 . merging ( with: HTTPFields ( options. global. headers) )
172172
173+ checkAuthorizationHeader ( _headers)
174+
173175 // default storage key uses the supabase project ref as a namespace
174176 let defaultStorageKey = " sb- \( supabaseURL. host!. split ( separator: " . " ) [ 0 ] ) -auth-token "
175177
@@ -351,7 +353,7 @@ public final class SupabaseClient: Sendable {
351353 let token = try ? await _getAccessToken ( )
352354
353355 var request = request
354- if let token {
356+ if let token, isJWT ( token ) , request . value ( forHTTPHeaderField : " Authorization " ) == nil {
355357 request. setValue ( " Bearer \( token) " , forHTTPHeaderField: " Authorization " )
356358 }
357359 return request
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