-
Notifications
You must be signed in to change notification settings - Fork 26
Expand file tree
/
Copy pathJWT.swift
More file actions
120 lines (93 loc) · 4.43 KB
/
Copy pathJWT.swift
File metadata and controls
120 lines (93 loc) · 4.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//
// Copyright (c) 2021-Present, Okta, Inc. and/or its affiliates. All rights reserved.
// The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
//
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and limitations under the License.
//
import Foundation
#if !COCOAPODS
@_exported import JSON
#endif
/// Represents the contents of a JWT token, providing access to its payload contents.
public struct JWT: RawRepresentable, Sendable, Codable, HasClaims, Expires {
public typealias ClaimType = JWTClaim
public typealias RawValue = String
/// The raw string representation of this JWT token.
public let rawValue: String
/// The date this token will expire.
public var expirationTime: Date? { self[.expirationTime] }
/// The issuer claim for this token.
public var issuer: String? { self[.issuer] }
/// The intended audience for this token.
public var audience: String? { self[.audience] }
/// The date this token was issued.
public var issuedAt: Date? { self[.issuedAt] }
/// The date before which this token should not yet beconsidered valid.
public var notBefore: Date? { self[.notBefore] }
/// The time interval in which the token will expire.
public var expiresIn: TimeInterval { self[.expiresIn] ?? 0 }
/// The array of scopes this token is valid for.
public var scope: [String]? { self[.scope] ?? self["scp"] }
/// The authentication context class reference.
///
/// The ``JWTClaim/authContextClassReference`` claim (or `acr` in string form) defines a special authentication context reference which indicates additional policy choices requested when authenticating a user.
public var authenticationContext: String? { self[.authContextClassReference] }
/// JWT header information describing the contents of the token.
public struct Header: Sendable, Decodable {
/// The ID of the key used to sign this JWT token.
public let keyId: String
/// The signing algorithm used to sign this JWT token.
public let algorithm: JWK.Algorithm
enum CodingKeys: String, CodingKey {
case keyId = "kid"
case algorithm = "alg"
}
}
/// Initializer to create a JWT instance from a raw string value.
/// - Parameter rawValue: Raw string value of the JWT.
public init?(rawValue: RawValue) {
try? self.init(rawValue)
}
/// Verifies the JWT token using the given ``JWK`` key.
/// - Parameter keySet: JWK keyset which should be used to verify this token.
/// - Returns: Returns whether or not signing passes for this token/key combination.
/// - Throws: ``JWTError``
public func validate(using keySet: JWKS) throws -> Bool {
return try JWK.validator.validate(token: self, using: keySet)
}
/// The header portion of the JWT token.
public let header: Header
/// Designated initializer, accepting the token string.
/// - Parameter token: Token string.
public init(_ token: String) throws {
rawValue = token
let components = JWT.tokenComponents(from: rawValue)
guard components.count >= 2, components.count <= 3
else {
throw JWTError.badTokenStructure
}
guard let headerData = Data(base64Encoded: components[0]),
let payloadData = Data(base64Encoded: components[1])
else { throw JWTError.invalidBase64Encoding }
self.header = try JSONDecoder().decode(JWT.Header.self, from: headerData)
self.body = try JSON(payloadData)
if components.count == 3 {
self.signature = components[2]
}
}
/// Raw paylaod of claims, as a dictionary representation.
public var body: JSON
/// Signature of the JWT token.
public var signature: String?
public var payload: [String: any Sendable] { body.payload }
static func tokenComponents(from token: String) -> [String] {
token
.components(separatedBy: ".")
.map(\.base64URLDecoded)
}
}