Parse JWT Header

This commit is contained in:
2018-12-29 14:39:45 -04:00
parent 58c1b62110
commit ba099e66c2
5 changed files with 82 additions and 26 deletions

View File

@@ -7,18 +7,15 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
821E160721D7ED3F00E2D71A /* TokenDeserializer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821E160621D7ED3F00E2D71A /* TokenDeserializer.swift */; };
82CAF46A21D7D41800FED241 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF46921D7D41800FED241 /* AppDelegate.swift */; }; 82CAF46A21D7D41800FED241 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF46921D7D41800FED241 /* AppDelegate.swift */; };
82CAF46C21D7D41C00FED241 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 82CAF46B21D7D41C00FED241 /* Assets.xcassets */; }; 82CAF46C21D7D41C00FED241 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 82CAF46B21D7D41C00FED241 /* Assets.xcassets */; };
82CAF46F21D7D41C00FED241 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 82CAF46D21D7D41C00FED241 /* MainMenu.xib */; }; 82CAF46F21D7D41C00FED241 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 82CAF46D21D7D41C00FED241 /* MainMenu.xib */; };
82CAF47B21D7D41C00FED241 /* JotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF47A21D7D41C00FED241 /* JotTests.swift */; }; 82CAF47B21D7D41C00FED241 /* JotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF47A21D7D41C00FED241 /* JotTests.swift */; };
82CAF48821D7D56D00FED241 /* JWT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48721D7D56D00FED241 /* JWT.swift */; }; 82CAF48821D7D56D00FED241 /* JWT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48721D7D56D00FED241 /* JWT.swift */; };
82CAF48921D7D57700FED241 /* JWT.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48721D7D56D00FED241 /* JWT.swift */; };
82CAF48B21D7D5B700FED241 /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48A21D7D5B700FED241 /* Payload.swift */; }; 82CAF48B21D7D5B700FED241 /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48A21D7D5B700FED241 /* Payload.swift */; };
82CAF48D21D7D60100FED241 /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48C21D7D60000FED241 /* Header.swift */; }; 82CAF48D21D7D60100FED241 /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48C21D7D60000FED241 /* Header.swift */; };
82CAF48F21D7D81800FED241 /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48E21D7D81800FED241 /* Signature.swift */; }; 82CAF48F21D7D81800FED241 /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48E21D7D81800FED241 /* Signature.swift */; };
82CAF49021D7DC5100FED241 /* Payload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48A21D7D5B700FED241 /* Payload.swift */; };
82CAF49121D7DC5300FED241 /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48C21D7D60000FED241 /* Header.swift */; };
82CAF49221D7DC5600FED241 /* Signature.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CAF48E21D7D81800FED241 /* Signature.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -32,6 +29,7 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
821E160621D7ED3F00E2D71A /* TokenDeserializer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenDeserializer.swift; sourceTree = "<group>"; };
82CAF46621D7D41800FED241 /* Jot.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Jot.app; sourceTree = BUILT_PRODUCTS_DIR; }; 82CAF46621D7D41800FED241 /* Jot.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Jot.app; sourceTree = BUILT_PRODUCTS_DIR; };
82CAF46921D7D41800FED241 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; 82CAF46921D7D41800FED241 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
82CAF46B21D7D41C00FED241 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 82CAF46B21D7D41C00FED241 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -113,6 +111,7 @@
82CAF48A21D7D5B700FED241 /* Payload.swift */, 82CAF48A21D7D5B700FED241 /* Payload.swift */,
82CAF48C21D7D60000FED241 /* Header.swift */, 82CAF48C21D7D60000FED241 /* Header.swift */,
82CAF48E21D7D81800FED241 /* Signature.swift */, 82CAF48E21D7D81800FED241 /* Signature.swift */,
821E160621D7ED3F00E2D71A /* TokenDeserializer.swift */,
); );
path = Token; path = Token;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -224,6 +223,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
821E160721D7ED3F00E2D71A /* TokenDeserializer.swift in Sources */,
82CAF48D21D7D60100FED241 /* Header.swift in Sources */, 82CAF48D21D7D60100FED241 /* Header.swift in Sources */,
82CAF48B21D7D5B700FED241 /* Payload.swift in Sources */, 82CAF48B21D7D5B700FED241 /* Payload.swift in Sources */,
82CAF46A21D7D41800FED241 /* AppDelegate.swift in Sources */, 82CAF46A21D7D41800FED241 /* AppDelegate.swift in Sources */,
@@ -236,11 +236,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
82CAF49121D7DC5300FED241 /* Header.swift in Sources */,
82CAF49021D7DC5100FED241 /* Payload.swift in Sources */,
82CAF47B21D7D41C00FED241 /* JotTests.swift in Sources */, 82CAF47B21D7D41C00FED241 /* JotTests.swift in Sources */,
82CAF49221D7DC5600FED241 /* Signature.swift in Sources */,
82CAF48921D7D57700FED241 /* JWT.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@@ -23,7 +23,9 @@ enum JwtType: String, Codable {
} }
enum SigningAlgorithm: String, Codable { enum SigningAlgorithm: String, Codable {
case rsa = "RSA" case rsa256 = "RS256"
case hmac = "HMAC" case rsa384 = "RS384"
case ecdsa = "ECDSA" case rsa512 = "RS512"
case hmac256 = "HM256"
case ecdsa256 = "ES256"
} }

View File

@@ -9,19 +9,13 @@
import Foundation import Foundation
class JWT { class JWT {
private let header: Header let header: Header
private let payload: Payload let payload: Payload
private let signature: Signature let signature: Signature
init(header: Header, payload: Payload, signature: Signature) { init(header: Header, payload: Payload, signature: Signature) {
self.header = header self.header = header
self.payload = payload self.payload = payload
self.signature = signature self.signature = signature
} }
init?(token: String) {
return nil
}
} }

View File

@@ -0,0 +1,62 @@
//
// TokenDeserializer.swift
// Jot
//
// Created by James Griffin-Allwood on 2018-12-29.
// Copyright © 2018 James Griffin-Allwood. All rights reserved.
//
import Foundation
import os.log
extension JWT {
convenience init?(token: String) {
let decoder = JSONDecoder()
let jwtParts = token.split(separator: ".")
guard jwtParts.count == 3 else {
os_log("Invalid JWT format. Requires 3 parts", log: OSLog.default, type: .error)
return nil
}
let base64Header = String(jwtParts[0])
guard let headerData = Data(base64Encoded: base64Header.base64Pad()) else {
os_log("Header must be Base64 Encoded", log: OSLog.default, type: .error)
return nil
}
let base64Payload = String(jwtParts[1])
guard let payloadData = Data(base64Encoded: base64Payload.base64Pad()) else {
os_log("Payload must be Base64 Encoded", log: OSLog.default, type: .error)
return nil
}
// TODO: Base64 Decoding the signature fails. Leave alone until adding Token Validation
// let base64Signature = String(jwtParts[2])
// guard let signatureData = Data(base64Encoded: base64Signature.base64Pad()) else {
// os_log("Signature must be Base64 Encoded", log: OSLog.default, type: .error)
// return nil
// }
do {
let header = try decoder.decode(Header.self, from: headerData)
let payload = try decoder.decode(Payload.self, from: payloadData)
let signature = Signature()
self.init(header: header, payload: payload, signature: signature)
} catch {
os_log("Unable to deserialize JWT: %@", log: OSLog.default, type: .error, error.localizedDescription)
return nil
}
}
}
extension String {
func base64Pad() -> String {
var paddedString = self
if self.count % 4 != 0 {
let charactersToAdd = 4 - self.count % 4
paddedString.append(contentsOf: repeatElement("=", count: charactersToAdd))
}
return paddedString
}
}

View File

@@ -10,13 +10,15 @@ import XCTest
@testable import Jot @testable import Jot
class JotTests: XCTestCase { class JotTests: XCTestCase {
var jwt: JWT?
override func setUp() {
self.jwt = JWT(token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqZ3JpZmZpbiIsIm5hbWUiOiJKYW1lcyBHcmlmZmluIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.DXfNW7Gzvie7LIl5-HSG9clm3sjeglYG2uNmsPvcNerBQbzLI06vys4YqqCMtrWKEFij_y2hKGP9YFdHSRMdtyVcGEXmYP6H6E62uGhASmGFzEHeSa6B1X1-v8lRt_YpIGEXhkUnxfhdcQ90HJlNfajv0ndnRFk8YJxRkmr99OI")
}
func testJWTInit() { func testJWTInit() {
XCTAssertNotNil(self.jwt) guard let jwt = JWT(token: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJqZ3JpZmZpbiIsIm5hbWUiOiJKYW1lcyBHcmlmZmluIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.DXfNW7Gzvie7LIl5-HSG9clm3sjeglYG2uNmsPvcNerBQbzLI06vys4YqqCMtrWKEFij_y2hKGP9YFdHSRMdtyVcGEXmYP6H6E62uGhASmGFzEHeSa6B1X1-v8lRt_YpIGEXhkUnxfhdcQ90HJlNfajv0ndnRFk8YJxRkmr99OI") else {
XCTFail()
return
}
XCTAssertEqual(jwt.header.alg, SigningAlgorithm.rsa256)
XCTAssertEqual(jwt.header.typ, JwtType.jwt)
} }
} }