Skip to content

Commit 99c3624

Browse files
committed
Include more documentation
1 parent ede8828 commit 99c3624

File tree

5 files changed

+191
-149
lines changed

5 files changed

+191
-149
lines changed

JWT.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
279D63AF1AD07FFF0024E2BC /* JWTTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279D63AE1AD07FFF0024E2BC /* JWTTests.swift */; };
1313
279D63B91AD0803F0024E2BC /* JWT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279D63B81AD0803F0024E2BC /* JWT.swift */; };
1414
279D63BB1AD0E3FA0024E2BC /* Claims.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279D63BA1AD0E3FA0024E2BC /* Claims.swift */; };
15+
279D63BD1AD0ED750024E2BC /* Decode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279D63BC1AD0ED750024E2BC /* Decode.swift */; };
16+
279D63BF1AD0EDC00024E2BC /* Base64.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279D63BE1AD0EDC00024E2BC /* Base64.swift */; };
1517
885619E9E1C342A9D8BD77B7 /* Pods_JWT.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 540942F3614C41E3827F2013 /* Pods_JWT.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
1618
EBEC5851F5183DF2D7BFE1AF /* Pods_JWTTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE8198B6E30BA6B8F8125FA7 /* Pods_JWTTests.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
1719
/* End PBXBuildFile section */
@@ -35,6 +37,8 @@
3537
279D63AE1AD07FFF0024E2BC /* JWTTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JWTTests.swift; sourceTree = "<group>"; };
3638
279D63B81AD0803F0024E2BC /* JWT.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JWT.swift; sourceTree = "<group>"; };
3739
279D63BA1AD0E3FA0024E2BC /* Claims.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Claims.swift; sourceTree = "<group>"; };
40+
279D63BC1AD0ED750024E2BC /* Decode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Decode.swift; sourceTree = "<group>"; };
41+
279D63BE1AD0EDC00024E2BC /* Base64.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Base64.swift; sourceTree = "<group>"; };
3842
3BD8D638895FE8AF4FDDA8A9 /* Pods-JWTTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JWTTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-JWTTests/Pods-JWTTests.debug.xcconfig"; sourceTree = "<group>"; };
3943
540942F3614C41E3827F2013 /* Pods_JWT.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_JWT.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4044
56671E3EAC540766DE31974E /* Pods-JWT.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JWT.release.xcconfig"; path = "Pods/Target Support Files/Pods-JWT/Pods-JWT.release.xcconfig"; sourceTree = "<group>"; };
@@ -91,6 +95,8 @@
9195
children = (
9296
279D63A11AD07FFF0024E2BC /* JWT.h */,
9397
279D63B81AD0803F0024E2BC /* JWT.swift */,
98+
279D63BC1AD0ED750024E2BC /* Decode.swift */,
99+
279D63BE1AD0EDC00024E2BC /* Base64.swift */,
94100
279D63BA1AD0E3FA0024E2BC /* Claims.swift */,
95101
279D639F1AD07FFF0024E2BC /* Supporting Files */,
96102
);
@@ -348,7 +354,9 @@
348354
isa = PBXSourcesBuildPhase;
349355
buildActionMask = 2147483647;
350356
files = (
357+
279D63BF1AD0EDC00024E2BC /* Base64.swift in Sources */,
351358
279D63BB1AD0E3FA0024E2BC /* Claims.swift in Sources */,
359+
279D63BD1AD0ED750024E2BC /* Decode.swift in Sources */,
352360
279D63B91AD0803F0024E2BC /* JWT.swift in Sources */,
353361
);
354362
runOnlyForDeploymentPostprocessing = 0;

JWT/Base64.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Foundation
2+
3+
4+
/// URI Safe base64 encode
5+
func base64encode(input:NSData) -> String {
6+
let data = input.base64EncodedDataWithOptions(NSDataBase64EncodingOptions(0))
7+
let string = NSString(data: data, encoding: NSUTF8StringEncoding) as String
8+
return string
9+
.stringByReplacingOccurrencesOfString("+", withString: "-", options: NSStringCompareOptions(0), range: nil)
10+
.stringByReplacingOccurrencesOfString("/", withString: "_", options: NSStringCompareOptions(0), range: nil)
11+
.stringByReplacingOccurrencesOfString("=", withString: "", options: NSStringCompareOptions(0), range: nil)
12+
}
13+
14+
/// URI Safe base64 decode
15+
func base64decode(input:String) -> NSData? {
16+
let rem = countElements(input) % 4
17+
18+
var ending = ""
19+
if rem > 0 {
20+
let amount = 4 - rem
21+
ending = String(count: amount, repeatedValue: Character("="))
22+
}
23+
24+
let base64 = input.stringByReplacingOccurrencesOfString("-", withString: "+", options: NSStringCompareOptions(0), range: nil)
25+
.stringByReplacingOccurrencesOfString("_", withString: "/", options: NSStringCompareOptions(0), range: nil) + ending
26+
27+
return NSData(base64EncodedString: base64, options: NSDataBase64DecodingOptions(0))
28+
}

JWT/Decode.swift

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import Foundation
2+
3+
4+
/// Failure reasons from decoding a JWT
5+
public enum InvalidToken : Printable {
6+
/// Decoding the JWT itself failed
7+
case DecodeError(String)
8+
9+
/// The JWT uses an unsupported algorithm
10+
case InvalidAlgorithm
11+
12+
/// An invalid key was used when trying to validate a JWT
13+
case InvalidKey
14+
15+
/// The issued claim has expired
16+
case ExpiredSignature
17+
18+
/// The issued claim is for the future
19+
case ImmatureSignature
20+
21+
/// The claim is for the future
22+
case InvalidIssuedAt
23+
24+
/// The audience of the claim doesn't match
25+
case InvalidAudience
26+
27+
/// The issuer claim failed to verify
28+
case InvalidIssuer
29+
30+
/// Returns a readable description of the error
31+
public var description:String {
32+
switch self {
33+
case .DecodeError(let error):
34+
return "Decode Error: \(error)"
35+
case .InvalidIssuer:
36+
return "Invalid Issuer"
37+
case .ExpiredSignature:
38+
return "Expired Signature"
39+
case .ImmatureSignature:
40+
return "The token is not yet valid (not before claim)"
41+
case .InvalidIssuedAt:
42+
return "Issued at claim (iat) is in the future"
43+
case InvalidAudience:
44+
return "Invalid Audience"
45+
case InvalidAlgorithm:
46+
return "Unsupported Algorithm"
47+
case InvalidKey:
48+
return "Invalid Key"
49+
}
50+
}
51+
}
52+
53+
54+
/// Result from decoding a JWT
55+
public enum DecodeResult {
56+
/// Decoding succeeded
57+
case Success(Payload)
58+
59+
/// Decoding failed, take a look at the invalid token reason
60+
case Failure(InvalidToken)
61+
}
62+
63+
64+
/// Decode a JWT
65+
public func decode(jwt:String, key:String? = nil, verify:Bool = true, audience:String? = nil, issuer:String? = nil) -> DecodeResult {
66+
switch load(jwt) {
67+
case let .Success(header, payload, signature, signatureInput):
68+
if verify {
69+
if let failure = validateClaims(payload, audience, issuer) ?? verifySignature(header, signatureInput, signature, key) {
70+
return .Failure(failure)
71+
}
72+
}
73+
74+
return .Success(payload)
75+
case .Failure(let failure):
76+
return .Failure(failure)
77+
}
78+
}
79+
80+
// MARK: Parsing a JWT
81+
82+
enum LoadResult {
83+
case Success(header:Payload, payload:Payload, signature:NSData, signatureInput:String)
84+
case Failure(InvalidToken)
85+
}
86+
87+
func load(jwt:String) -> LoadResult {
88+
let segments = jwt.componentsSeparatedByString(".")
89+
if segments.count != 3 {
90+
return .Failure(.DecodeError("Not enough segments"))
91+
}
92+
93+
let headerSegment = segments[0]
94+
let payloadSegment = segments[1]
95+
let signatureSegment = segments[2]
96+
let signatureInput = "\(headerSegment).\(payloadSegment)"
97+
98+
let headerData = base64decode(headerSegment)
99+
if headerData == nil {
100+
return .Failure(.DecodeError("Header is not correctly encoded as base64"))
101+
}
102+
103+
let header = NSJSONSerialization.JSONObjectWithData(headerData!, options: NSJSONReadingOptions(0), error: nil) as? Payload
104+
if header == nil {
105+
return .Failure(.DecodeError("Invalid header"))
106+
}
107+
108+
let payloadData = base64decode(payloadSegment)
109+
if payloadData == nil {
110+
return .Failure(.DecodeError("Payload is not correctly encoded as base64"))
111+
}
112+
113+
let payload = NSJSONSerialization.JSONObjectWithData(payloadData!, options: NSJSONReadingOptions(0), error: nil) as? Payload
114+
if payload == nil {
115+
return .Failure(.DecodeError("Invalid payload"))
116+
}
117+
118+
let signature = base64decode(signatureSegment)
119+
if signature == nil {
120+
return .Failure(.DecodeError("Signature is not correctly encoded as base64"))
121+
}
122+
123+
return .Success(header:header!, payload:payload!, signature:signature!, signatureInput:signatureInput)
124+
}
125+
126+
// MARK: Signature Verification
127+
128+
func verifySignature(header:Payload, signingInput:String, signature:NSData, key:String?) -> InvalidToken? {
129+
if let alg = header["alg"] as? String {
130+
if let algoritm = Algorithm.algorithm(alg, key: key) {
131+
if algoritm.verify(signingInput, signature: signature) {
132+
return nil
133+
} else {
134+
return .InvalidKey
135+
}
136+
}
137+
138+
return .InvalidAlgorithm
139+
}
140+
141+
return .DecodeError("Missing Algorithm")
142+
}

0 commit comments

Comments
 (0)