diff --git a/.github/actions/test/action.yml b/.github/actions/test/action.yml index c38e152..b106acf 100644 --- a/.github/actions/test/action.yml +++ b/.github/actions/test/action.yml @@ -24,18 +24,6 @@ runs: run: xcodebuild -version | tee .xcode-version shell: bash - - id: restore-carthage-cache - name: Restore Carthage cache - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 - with: - path: Carthage - key: carthage-${{ inputs.platform }}-${{ hashFiles('Cartfile.resolved') }}-${{ hashFiles('.xcode-version') }}-v1 - - - name: Install dependencies - if: steps.restore-carthage-cache.outputs.cache-hit != 'true' - run: carthage bootstrap --platform ${{ inputs.platform }} --use-xcframeworks --no-use-binaries --cache-builds - shell: bash - - name: Run tests uses: mxcl/xcodebuild@6e60022a0cbe8c89278be2dd1773a2f68e7c5c87 with: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 97e53cc..0ba45be 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,8 +5,5 @@ ## Environment setup -We use [Carthage](https://github.com/Carthage/Carthage) to manage JWTDecode.swift's dependencies. - 1. Clone this repository and enter its root directory. -2. Run `carthage bootstrap --use-xcframeworks` to fetch and build the dependencies. -3. Open `JWTDecode.xcodeproj` in Xcode. +2. Open `JWTDecode.xcodeproj` in Xcode. diff --git a/Cartfile.private b/Cartfile.private deleted file mode 100644 index 2715491..0000000 --- a/Cartfile.private +++ /dev/null @@ -1,2 +0,0 @@ -github "Quick/Quick" ~> 7.0 -github "Quick/Nimble" ~> 12.0 diff --git a/Cartfile.resolved b/Cartfile.resolved deleted file mode 100644 index 87c0c61..0000000 --- a/Cartfile.resolved +++ /dev/null @@ -1,2 +0,0 @@ -github "Quick/Nimble" "v12.0.1" -github "Quick/Quick" "v7.0.2" diff --git a/JWTDecode.xcodeproj/project.pbxproj b/JWTDecode.xcodeproj/project.pbxproj index 22d1168..15c16f0 100644 --- a/JWTDecode.xcodeproj/project.pbxproj +++ b/JWTDecode.xcodeproj/project.pbxproj @@ -14,18 +14,6 @@ 5C1B5D8B238711A60076E46B /* JWTHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57EA5A04237EBBCB00264C01 /* JWTHelper.swift */; }; 5C1B5D8D238711B10076E46B /* JWTDecodeSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57EA5A02237EBBCA00264C01 /* JWTDecodeSpec.swift */; }; 5C1B5D8E238711B30076E46B /* JWTHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57EA5A04237EBBCB00264C01 /* JWTHelper.swift */; }; - 5CE9C9FB26FC1E8F005A75FA /* Nimble.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE9C9F926FC1E8F005A75FA /* Nimble.xcframework */; }; - 5CE9C9FC26FC1E8F005A75FA /* Quick.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE9C9FA26FC1E8F005A75FA /* Quick.xcframework */; }; - 5CE9CA0126FC1ED3005A75FA /* Nimble.xcframework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5CE9C9F926FC1E8F005A75FA /* Nimble.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 5CE9CA0226FC1ED5005A75FA /* Quick.xcframework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5CE9C9FA26FC1E8F005A75FA /* Quick.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 5CE9CA0326FC1FF5005A75FA /* Nimble.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE9C9F926FC1E8F005A75FA /* Nimble.xcframework */; }; - 5CE9CA0426FC1FF5005A75FA /* Quick.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE9C9FA26FC1E8F005A75FA /* Quick.xcframework */; }; - 5CE9CA0526FC1FFB005A75FA /* Nimble.xcframework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5CE9C9F926FC1E8F005A75FA /* Nimble.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 5CE9CA0626FC1FFB005A75FA /* Quick.xcframework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5CE9C9FA26FC1E8F005A75FA /* Quick.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 5CE9CA0726FC2009005A75FA /* Nimble.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE9C9F926FC1E8F005A75FA /* Nimble.xcframework */; }; - 5CE9CA0826FC2009005A75FA /* Quick.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE9C9FA26FC1E8F005A75FA /* Quick.xcframework */; }; - 5CE9CA0926FC200D005A75FA /* Nimble.xcframework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5CE9C9F926FC1E8F005A75FA /* Nimble.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 5CE9CA0A26FC200D005A75FA /* Quick.xcframework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5CE9C9FA26FC1E8F005A75FA /* Quick.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 5CE9CA0C26FC22F8005A75FA /* JWTDecode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F0068E21B3B46240048928E /* JWTDecode.framework */; }; 5CE9CA0D26FC23B1005A75FA /* JWTDecode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5F0069091B3B532E0048928E /* JWTDecode.framework */; }; 5CE9CA0E26FC23B8005A75FA /* JWTDecode.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 918A8E5B1D63D2E1001F787B /* JWTDecode.framework */; }; @@ -66,48 +54,10 @@ }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - 5F2614DC1C05FEAD0068DE71 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 5CE9CA0126FC1ED3005A75FA /* Nimble.xcframework in CopyFiles */, - 5CE9CA0226FC1ED5005A75FA /* Quick.xcframework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 5F2614DF1C05FEDA0068DE71 /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 5CE9CA0526FC1FFB005A75FA /* Nimble.xcframework in CopyFiles */, - 5CE9CA0626FC1FFB005A75FA /* Quick.xcframework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 918A8E801D63DB37001F787B /* CopyFiles */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 5CE9CA0926FC200D005A75FA /* Nimble.xcframework in CopyFiles */, - 5CE9CA0A26FC200D005A75FA /* Quick.xcframework in CopyFiles */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 57EA5A02237EBBCA00264C01 /* JWTDecodeSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JWTDecodeSpec.swift; path = JWTDecodeTests/JWTDecodeSpec.swift; sourceTree = SOURCE_ROOT; }; 57EA5A04237EBBCB00264C01 /* JWTHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = JWTHelper.swift; path = JWTDecodeTests/JWTHelper.swift; sourceTree = SOURCE_ROOT; }; 5CDF405828517BE2003840E6 /* JWTDecode.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = JWTDecode.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 5CE9C9F926FC1E8F005A75FA /* Nimble.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Nimble.xcframework; path = Carthage/Build/Nimble.xcframework; sourceTree = ""; }; - 5CE9C9FA26FC1E8F005A75FA /* Quick.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Quick.xcframework; path = Carthage/Build/Quick.xcframework; sourceTree = ""; }; 5F0068E21B3B46240048928E /* JWTDecode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JWTDecode.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5F0068E61B3B46240048928E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 5F0068ED1B3B46240048928E /* JWTDecode-iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "JWTDecode-iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -115,26 +65,18 @@ 5F0069021B3B511F0048928E /* JWTDecode.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JWTDecode.swift; sourceTree = ""; }; 5F0069091B3B532E0048928E /* JWTDecode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JWTDecode.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5F0069131B3B532E0048928E /* JWTDecode-macOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "JWTDecode-macOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 5F2496A31D64A91300A1C6E2 /* Cartfile.resolved */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile.resolved; sourceTree = ""; }; 5F2496A41D64A91300A1C6E2 /* JWTDecode.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = JWTDecode.podspec; sourceTree = ""; }; 5F2496A51D64A91300A1C6E2 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; - 5F2496A61D64A91300A1C6E2 /* Cartfile.private */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile.private; sourceTree = ""; }; 5F2496A71D64A91300A1C6E2 /* Gemfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Gemfile; sourceTree = ""; }; 5F2496A81D64A91300A1C6E2 /* Gemfile.lock */ = {isa = PBXFileReference; lastKnownFileType = text; path = Gemfile.lock; sourceTree = ""; }; 5F2496AA1D64A91300A1C6E2 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 5F2496AB1D64A91300A1C6E2 /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = ""; }; 5F2496AC1D64A91300A1C6E2 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 5F2496AD1D64B88900A1C6E2 /* codecov.yml */ = {isa = PBXFileReference; lastKnownFileType = text; path = codecov.yml; sourceTree = ""; }; - 5F2614CD1C05FDDE0068DE71 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = SOURCE_ROOT; }; - 5F2614CE1C05FDDE0068DE71 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = SOURCE_ROOT; }; - 5F2614D41C05FE720068DE71 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/Mac/Quick.framework; sourceTree = SOURCE_ROOT; }; - 5F2614D51C05FE720068DE71 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/Mac/Nimble.framework; sourceTree = SOURCE_ROOT; }; 5FE49DCC1BA0D5F700DE57D3 /* JWT.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JWT.swift; sourceTree = ""; }; 5FE49DCF1BA0D66F00DE57D3 /* JWTDecodeError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JWTDecodeError.swift; sourceTree = ""; }; 918A8E5B1D63D2E1001F787B /* JWTDecode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JWTDecode.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 918A8E6C1D63D4E5001F787B /* JWTDecode-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "JWTDecode-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 918A8E7A1D63D86E001F787B /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/tvOS/Nimble.framework; sourceTree = SOURCE_ROOT; }; - 918A8E7B1D63D86E001F787B /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/tvOS/Quick.framework; sourceTree = SOURCE_ROOT; }; E390BAE22288E6AF00780D6C /* JWTDecode.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JWTDecode.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -150,8 +92,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5CE9C9FB26FC1E8F005A75FA /* Nimble.xcframework in Frameworks */, - 5CE9C9FC26FC1E8F005A75FA /* Quick.xcframework in Frameworks */, 5CE9CA0C26FC22F8005A75FA /* JWTDecode.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -167,8 +107,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5CE9CA0326FC1FF5005A75FA /* Nimble.xcframework in Frameworks */, - 5CE9CA0426FC1FF5005A75FA /* Quick.xcframework in Frameworks */, 5CE9CA0D26FC23B1005A75FA /* JWTDecode.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -184,8 +122,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5CE9CA0726FC2009005A75FA /* Nimble.xcframework in Frameworks */, - 5CE9CA0826FC2009005A75FA /* Quick.xcframework in Frameworks */, 5CE9CA0E26FC23B8005A75FA /* JWTDecode.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -200,15 +136,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 57F02C0B237ED39800D45E14 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 5CE9C9F926FC1E8F005A75FA /* Nimble.xcframework */, - 5CE9C9FA26FC1E8F005A75FA /* Quick.xcframework */, - ); - name = Frameworks; - sourceTree = ""; - }; 5F0068D81B3B46240048928E = { isa = PBXGroup; children = ( @@ -217,7 +144,6 @@ 5F0068F11B3B46240048928E /* JWTDecodeTests */, 5F0068E31B3B46240048928E /* Products */, 5F2496A11D64A8EE00A1C6E2 /* Supporting Files */, - 57F02C0B237ED39800D45E14 /* Frameworks */, ); sourceTree = ""; }; @@ -267,7 +193,6 @@ 5F0068F21B3B46240048928E /* Supporting Files */ = { isa = PBXGroup; children = ( - 5F2614D11C05FE0A0068DE71 /* Frameworks */, 5F0068F31B3B46240048928E /* Info.plist */, ); name = "Supporting Files"; @@ -277,10 +202,8 @@ isa = PBXGroup; children = ( 5F2496AD1D64B88900A1C6E2 /* codecov.yml */, - 5F2496A31D64A91300A1C6E2 /* Cartfile.resolved */, 5F2496A41D64A91300A1C6E2 /* JWTDecode.podspec */, 5F2496A51D64A91300A1C6E2 /* .gitignore */, - 5F2496A61D64A91300A1C6E2 /* Cartfile.private */, 5F2496A71D64A91300A1C6E2 /* Gemfile */, 5F2496A81D64A91300A1C6E2 /* Gemfile.lock */, 5F2496AA1D64A91300A1C6E2 /* README.md */, @@ -290,43 +213,6 @@ name = "Supporting Files"; sourceTree = ""; }; - 5F2614D11C05FE0A0068DE71 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 5F2614D31C05FE1B0068DE71 /* OSX */, - 5F2614D21C05FE160068DE71 /* iOS */, - 918A8E791D63D84A001F787B /* tvOS */, - ); - name = Frameworks; - sourceTree = ""; - }; - 5F2614D21C05FE160068DE71 /* iOS */ = { - isa = PBXGroup; - children = ( - 5F2614CD1C05FDDE0068DE71 /* Quick.framework */, - 5F2614CE1C05FDDE0068DE71 /* Nimble.framework */, - ); - name = iOS; - sourceTree = ""; - }; - 5F2614D31C05FE1B0068DE71 /* OSX */ = { - isa = PBXGroup; - children = ( - 5F2614D41C05FE720068DE71 /* Quick.framework */, - 5F2614D51C05FE720068DE71 /* Nimble.framework */, - ); - name = OSX; - sourceTree = ""; - }; - 918A8E791D63D84A001F787B /* tvOS */ = { - isa = PBXGroup; - children = ( - 918A8E7A1D63D86E001F787B /* Nimble.framework */, - 918A8E7B1D63D86E001F787B /* Quick.framework */, - ); - name = tvOS; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -387,7 +273,6 @@ 5F0068E91B3B46240048928E /* Sources */, 5F0068EA1B3B46240048928E /* Frameworks */, 5F0068EB1B3B46240048928E /* Resources */, - 5F2614DC1C05FEAD0068DE71 /* CopyFiles */, ); buildRules = ( ); @@ -425,7 +310,6 @@ 5F00690F1B3B532E0048928E /* Sources */, 5F0069101B3B532E0048928E /* Frameworks */, 5F0069111B3B532E0048928E /* Resources */, - 5F2614DF1C05FEDA0068DE71 /* CopyFiles */, ); buildRules = ( ); @@ -463,7 +347,6 @@ 918A8E681D63D4E5001F787B /* Sources */, 918A8E691D63D4E5001F787B /* Frameworks */, 918A8E6A1D63D4E5001F787B /* Resources */, - 918A8E801D63DB37001F787B /* CopyFiles */, ); buildRules = ( ); diff --git a/JWTDecodeTests/JWTDecodeSpec.swift b/JWTDecodeTests/JWTDecodeSpec.swift index fd03084..f42ef53 100644 --- a/JWTDecodeTests/JWTDecodeSpec.swift +++ b/JWTDecodeTests/JWTDecodeSpec.swift @@ -1,333 +1,396 @@ -import Quick -import Nimble +import XCTest import JWTDecode import Foundation -class JWTDecodeSpec: QuickSpec { - override class func spec() { - describe("decode") { - it("should tell a jwt is expired") { - expect(expiredJWT().expired).to(beTruthy()) - } - - it("should tell a jwt is not expired") { - expect(nonExpiredJWT().expired).to(beFalsy()) - } - - it("should tell a jwt is expired with a close enough timestamp") { - expect(jwtThatExpiresAt(date: Date()).expired).to(beTruthy()) - } - - it("should obtain payload") { - let jwt = jwt(withBody: ["sub": "myid", "name": "Shawarma Monk"]) - let payload = jwt.body as! [String: String] - expect(payload).to(equal(["sub": "myid", "name": "Shawarma Monk"])) - } - - it("should return original jwt string representation") { - let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjb20uc29td2hlcmUuZmFyLmJleW9uZDphcGki" - + "LCJpc3MiOiJhdXRoMCIsInVzZXJfcm9sZSI6ImFkbWluIn0.sS84motSLj9HNTgrCPcAjgZIQ99jXNN7_W9fEIIfxz0" - let jwt = try! decode(jwt: jwtString) - expect(jwt.string).to(equal(jwtString)) - } - - it("should return expire date") { - expect(expiredJWT().expiresAt).toNot(beNil()) - } - - it("should decode valid jwt") { - let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjb20uc29td2hlcmUuZmFyLmJleW9uZDphcGki" - + "LCJpc3MiOiJhdXRoMCIsInVzZXJfcm9sZSI6ImFkbWluIn0.sS84motSLj9HNTgrCPcAjgZIQ99jXNN7_W9fEIIfxz0" - expect(try! decode(jwt: jwtString)).toNot(beNil()) - } - - it("should decode valid jwt with empty json body") { - let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.Et9HFtf9R3GEMA0IICOfFMVXY7kkTX1wr4qCyhIf58U" - expect(try! decode(jwt: jwtString)).toNot(beNil()) - } - - it("should raise exception with invalid base64 encoding") { - let invalidChar = "%" - let jwtString = "\(invalidChar).BODY.SIGNATURE" - expect { try decode(jwt: jwtString) } - .to(throwError { (error: Error) in - expect(error).to(beJWTDecodeError(.invalidBase64URL(invalidChar))) - }) - } - - it("should raise exception with invalid json in jwt") { - let jwtString = "HEADER.BODY.SIGNATURE" - expect { try decode(jwt: jwtString) } - .to(throwError { (error: Error) in - expect(error).to(beJWTDecodeError(.invalidJSON("HEADER"))) - }) - } - - it("should raise exception with missing parts") { - let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWIifQ" - expect { try decode(jwt: jwtString) } - .to(throwError { (error: Error) in - expect(error).to(beJWTDecodeError(.invalidPartCount(jwtString, 2))) - }) - } - } - - describe("jwt parts") { - let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWIifQ.xXcD7WOvUDHJ94E6aVHYgXdsJHLl2oW7Z" - + "Xm4QpVvXnY" - let sut = try! decode(jwt: jwtString) - - it("should return header") { - expect(sut.header as? [String: String]).to(equal(["alg": "HS256", "typ": "JWT"])) - } - - it("should return body") { - expect(sut.body as? [String: String]).to(equal(["sub": "sub"])) - } - - it("should return signature") { - expect(sut.signature).to(equal("xXcD7WOvUDHJ94E6aVHYgXdsJHLl2oW7ZXm4QpVvXnY")) - } +class JWTDecodeSpec: XCTestCase { + + func testExpiredJWT() { + let sut = expiredJWT() + XCTAssertTrue(sut.expired) + } + + func testNonExpiredJWT() { + let sut = nonExpiredJWT() + XCTAssertFalse(sut.expired) + } + + func testExpiredJWTWithCloseEnoughTimestamp() { + let sut = jwtThatExpiresAt(date: Date()) + XCTAssertTrue(sut.expired) + } + + func testObtainPayload() { + let jwt = jwt(withBody: ["sub": "myid", "name": "Shawarma Monk"]) + let payload = jwt.body as! [String: String] + XCTAssertEqual(payload, ["sub": "myid", "name": "Shawarma Monk"]) + } + + func testReturnOriginalJWTStringRepresentation() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjb20uc29td2hlcmUuZmFyLmJleW9uZDphcGki" + + "LCJpc3MiOiJhdXRoMCIsInVzZXJfcm9sZSI6ImFkbWluIn0.sS84motSLj9HNTgrCPcAjgZIQ99jXNN7_W9fEIIfxz0" + let jwt = try! decode(jwt: jwtString) + XCTAssertEqual(jwt.string, jwtString) + } + + func testReturnExpireDate() { + let sut = expiredJWT() + XCTAssertNotNil(sut.expiresAt) + } + + func testDecodeValidJWT() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjb20uc29td2hlcmUuZmFyLmJleW9uZDphcGki" + + "LCJpc3MiOiJhdXRoMCIsInVzZXJfcm9sZSI6ImFkbWluIn0.sS84motSLj9HNTgrCPcAjgZIQ99jXNN7_W9fEIIfxz0" + XCTAssertNotNil(try! decode(jwt: jwtString)) + } + + func testDecodeValidJWTWithEmptyJSONBody() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.Et9HFtf9R3GEMA0IICOfFMVXY7kkTX1wr4qCyhIf58U" + XCTAssertNotNil(try! decode(jwt: jwtString)) + } + + func testRaiseExceptionWithInvalidBase64Encoding() { + let invalidChar = "%" + let jwtString = "\(invalidChar).BODY.SIGNATURE" + XCTAssertThrowsError(try decode(jwt: jwtString)) { error in + XCTAssertEqual(error as? JWTDecodeError, .invalidBase64URL(invalidChar)) } - - describe("claims") { - var sut: JWT! - - describe("expiresAt claim") { - it("should handle expired jwt") { - sut = expiredJWT() - expect(sut.expiresAt).toNot(beNil()) - expect(sut.expired).to(beTruthy()) - } - - it("should handle non-expired jwt") { - sut = nonExpiredJWT() - expect(sut.expiresAt).toNot(beNil()) - expect(sut.expired).to(beFalsy()) - } - - it("should handle jwt without expiresAt claim") { - sut = jwt(withBody: ["sub": UUID().uuidString]) - expect(sut.expiresAt).to(beNil()) - expect(sut.expired).to(beFalsy()) - } - } - - describe("registered claims") { - let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUudXMuYXV0aDAuY29t" - + "Iiwic3ViIjoiYXV0aDB8MTAxMDEwMTAxMCIsImF1ZCI6Imh0dHBzOi8vZXhhbXBsZS51cy5hdXRoMC5jb20iLCJleHAiOjE" - + "zNzI2NzQzMzYsImlhdCI6MTM3MjYzODMzNiwianRpIjoicXdlcnR5MTIzNDU2IiwibmJmIjoxMzcyNjM4MzM2fQ.PmTa620" - + "SkKEawqtY8sFsxesdnN8C9ffFTmstfjPPR84" - let sut = try! decode(jwt: jwtString) - - it("should return issuer") { - expect(sut.issuer).to(equal("https://example.us.auth0.com")) - } - - it("should return subject") { - expect(sut.subject).to(equal("auth0|1010101010")) - } - - it("should return single audience") { - expect(sut.audience).to(equal(["https://example.us.auth0.com"])) - } - - context("multiple audiences") { - let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiaHR0cHM6Ly9leGFtcGxlLnVzLmF1dGgw" - + "LmNvbSIsImh0dHBzOi8vYXBpLmV4YW1wbGUudXMuYXV0aDAuY29tIl19.sw24la9mmCmykudpyE-U1Ar5bbyuDMyKaW" - + "ksSkBXhrM" - let sut = try! decode(jwt: jwtString) - - it("should return all audiences") { - expect(sut.audience).to(equal(["https://example.us.auth0.com", "https://api.example.us.auth0.com"])) - } - } - - it("should return issued at") { - expect(sut.issuedAt).to(equal(Date(timeIntervalSince1970: 1372638336))) - } - - it("should return not before") { - expect(sut.notBefore).to(equal(Date(timeIntervalSince1970: 1372638336))) - } - - it("should return jwt id") { - expect(sut.identifier).to(equal("qwerty123456")) - } - } - - describe("custom claim") { - beforeEach { - sut = jwt(withBody: ["sub": UUID().uuidString, - "custom_string_claim": "Shawarma Friday!", - "custom_integer_claim": 10, - "custom_integer_claim_0": 0, - "custom_integer_claim_1": 1, - "custom_integer_claim_string": "13", - "custom_double_claim": 3.1, - "custom_double_claim_0.0": 0.0, - "custom_double_claim_1.0": 1.0, - "custom_double_claim_string": "1.3", - "custom_boolean_claim_true": true, - "custom_boolean_claim_false": false]) - } - - it("should return claim by name") { - let claim = sut.claim(name: "custom_string_claim") - expect(claim.rawValue).toNot(beNil()) - } - - it("should return string claim") { - let claim = sut["custom_string_claim"] - expect(claim.string) == "Shawarma Friday!" - expect(claim.array) == ["Shawarma Friday!"] - expect(claim.integer).to(beNil()) - expect(claim.double).to(beNil()) - expect(claim.date).to(beNil()) - expect(claim.boolean).to(beNil()) - } - - it("should return integer claim") { - let claim = sut["custom_integer_claim"] - expect(claim.string).to(beNil()) - expect(claim.array).to(beNil()) - expect(claim.integer) == 10 - expect(claim.double) == 10.0 - expect(claim.date) == Date(timeIntervalSince1970: 10) - expect(claim.boolean).to(beNil()) - } - - it("should return '0' integer claim") { - let claim = sut["custom_integer_claim_0"] - expect(claim.string).to(beNil()) - expect(claim.array).to(beNil()) - expect(claim.integer) == 0 - expect(claim.double) == 0.0 - expect(claim.date) == Date(timeIntervalSince1970: 0) - expect(claim.boolean).to(beNil()) - } - - it("should return '1' integer claim") { - let claim = sut["custom_integer_claim_1"] - expect(claim.string).to(beNil()) - expect(claim.array).to(beNil()) - expect(claim.integer) == 1 - expect(claim.double) == 1.0 - expect(claim.date) == Date(timeIntervalSince1970: 1) - expect(claim.boolean).to(beNil()) - } - - it("should return integer as string claim") { - let claim = sut["custom_integer_claim_string"] - expect(claim.string) == "13" - expect(claim.array) == ["13"] - expect(claim.integer) == 13 - expect(claim.double) == 13.0 - expect(claim.date) == Date(timeIntervalSince1970: 13) - expect(claim.boolean).to(beNil()) - } - - it("should return double claim") { - let claim = sut["custom_double_claim"] - expect(claim.string).to(beNil()) - expect(claim.array).to(beNil()) - expect(claim.integer) == 3 - expect(claim.double) == 3.1 - expect(claim.date) == Date(timeIntervalSince1970: 3.1) - expect(claim.boolean).to(beNil()) - } - - it("should return '0.0' double claim") { - let claim = sut["custom_double_claim_0.0"] - expect(claim.string).to(beNil()) - expect(claim.array).to(beNil()) - expect(claim.integer) == 0 - expect(claim.double) == 0.0 - expect(claim.date) == Date(timeIntervalSince1970: 0) - expect(claim.boolean).to(beNil()) - } - - it("should return '1.0' double claim") { - let claim = sut["custom_double_claim_1.0"] - expect(claim.string).to(beNil()) - expect(claim.array).to(beNil()) - expect(claim.integer) == 1 - expect(claim.double) == 1.0 - expect(claim.date) == Date(timeIntervalSince1970: 1) - expect(claim.boolean).to(beNil()) - } - - it("should return double as string claim") { - let claim = sut["custom_double_claim_string"] - expect(claim.string) == "1.3" - expect(claim.array) == ["1.3"] - expect(claim.integer).to(beNil()) - expect(claim.double) == 1.3 - expect(claim.date) == Date(timeIntervalSince1970: 1.3) - expect(claim.boolean).to(beNil()) - } - - it("should return true boolean claim") { - let claim = sut["custom_boolean_claim_true"] - expect(claim.string).to(beNil()) - expect(claim.array).to(beNil()) - expect(claim.integer).to(beNil()) - expect(claim.double).to(beNil()) - expect(claim.date).to(beNil()) - expect(claim.boolean) == true - } - - it("should return false boolean claim") { - let claim = sut["custom_boolean_claim_false"] - expect(claim.string).to(beNil()) - expect(claim.array).to(beNil()) - expect(claim.integer).to(beNil()) - expect(claim.double).to(beNil()) - expect(claim.date).to(beNil()) - expect(claim.boolean) == false - } - - it("should return no value when claim is not present") { - let unknownClaim = sut["missing_claim"] - expect(unknownClaim.array).to(beNil()) - expect(unknownClaim.string).to(beNil()) - expect(unknownClaim.integer).to(beNil()) - expect(unknownClaim.double).to(beNil()) - expect(unknownClaim.date).to(beNil()) - expect(unknownClaim.boolean).to(beNil()) - } - - context("raw claim") { - var sut: JWT! - - beforeEach { - let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3NhbXBsZXMuYXV0aDAu" - + "Y29tIiwic3ViIjoiYXV0aDB8MTAxMDEwMTAxMCIsImF1ZCI6Imh0dHBzOi8vc2FtcGxlcy5hdXRoMC5jb20iLCJ" - + "leHAiOjEzNzI2NzQzMzYsImlhdCI6MTM3MjYzODMzNiwianRpIjoicXdlcnR5MTIzNDU2IiwibmJmIjoxMzcyNj" - + "M4MzM2LCJlbWFpbCI6InVzZXJAaG9zdC5jb20iLCJjdXN0b20iOlsxLDIsM119.JeMRyHLkcoiqGxd958B6PABK" - + "NvhOhIgw-kbjecmhR_E" - sut = try! decode(jwt: jwtString) - } - - it("should return email") { - expect(sut["email"].string) == "user@host.com" - } - - it("should return array") { - expect(sut["custom"].rawValue as? [Int]).toNot(beNil()) - } - } - } + } + + func testRaiseExceptionWithInvalidJSONInJWT() { + let jwtString = "HEADER.BODY.SIGNATURE" + XCTAssertThrowsError(try decode(jwt: jwtString)) { error in + XCTAssertEqual(error as? JWTDecodeError, .invalidJSON("HEADER")) } } -} - -public func beJWTDecodeError(_ code: JWTDecodeError) -> Nimble.Predicate { - return Predicate.define("be jwt decode error <\(code)>") { expression, failureMessage -> PredicateResult in - guard let actual = try expression.evaluate() as? JWTDecodeError else { - return PredicateResult(status: .doesNotMatch, message: failureMessage) + + func testRaiseExceptionWithMissingParts() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWIifQ" + XCTAssertThrowsError(try decode(jwt: jwtString)) { error in + XCTAssertEqual(error as? JWTDecodeError, .invalidPartCount(jwtString, 2)) } - return PredicateResult(bool: actual == code, message: failureMessage) + } + + func testReturnHeader() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWIifQ.xXcD7WOvUDHJ94E6aVHYgXdsJHLl2oW7Z" + + "Xm4QpVvXnY" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut.header as? [String: String], ["alg": "HS256", "typ": "JWT"]) + } + + func testReturnBody() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWIifQ.xXcD7WOvUDHJ94E6aVHYgXdsJHLl2oW7Z" + + "Xm4QpVvXnY" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut.body as? [String: String], ["sub": "sub"]) + } + + func testReturnSignature() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJzdWIifQ.xXcD7WOvUDHJ94E6aVHYgXdsJHLl2oW7Z" + + "Xm4QpVvXnY" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut.signature, "xXcD7WOvUDHJ94E6aVHYgXdsJHLl2oW7ZXm4QpVvXnY") + } + + func testExpiresAtClaimExpiredJWT() { + let sut = expiredJWT() + XCTAssertNotNil(sut.expiresAt) + XCTAssertTrue(sut.expired) + } + + func testExpiresAtClaimNonExpiredJWT() { + let sut = nonExpiredJWT() + XCTAssertNotNil(sut.expiresAt) + XCTAssertFalse(sut.expired) + } + + func testExpiresAtClaimJWTWithoutExpiresAtClaim() { + let sut = jwt(withBody: ["sub": UUID().uuidString]) + XCTAssertNil(sut.expiresAt) + XCTAssertFalse(sut.expired) + } + + func testIssuerClaim() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUudXMuYXV0aDAuY29t" + + "Iiwic3ViIjoiYXV0aDB8MTAxMDEwMTAxMCIsImF1ZCI6Imh0dHBzOi8vZXhhbXBsZS51cy5hdXRoMC5jb20iLCJleHAiOjE" + + "zNzI2NzQzMzYsImlhdCI6MTM3MjYzODMzNiwianRpIjoicXdlcnR5MTIzNDU2IiwibmJmIjoxMzcyNjM4MzM2fQ.PmTa620" + + "SkKEawqtY8sFsxesdnN8C9ffFTmstfjPPR84" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut.issuer, "https://example.us.auth0.com") + } + + func testSubjectClaim() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUudXMuYXV0aDAuY29t" + + "Iiwic3ViIjoiYXV0aDB8MTAxMDEwMTAxMCIsImF1ZCI6Imh0dHBzOi8vZXhhbXBsZS51cy5hdXRoMC5jb20iLCJleHAiOjE" + + "zNzI2NzQzMzYsImlhdCI6MTM3MjYzODMzNiwianRpIjoicXdlcnR5MTIzNDU2IiwibmJmIjoxMzcyNjM4MzM2fQ.PmTa620" + + "SkKEawqtY8sFsxesdnN8C9ffFTmstfjPPR84" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut.subject, "auth0|1010101010") + } + + func testSingleAudienceClaim() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUudXMuYXV0aDAuY29t" + + "Iiwic3ViIjoiYXV0aDB8MTAxMDEwMTAxMCIsImF1ZCI6Imh0dHBzOi8vZXhhbXBsZS51cy5hdXRoMC5jb20iLCJleHAiOjE" + + "zNzI2NzQzMzYsImlhdCI6MTM3MjYzODMzNiwianRpIjoicXdlcnR5MTIzNDU2IiwibmJmIjoxMzcyNjM4MzM2fQ.PmTa620" + + "SkKEawqtY8sFsxesdnN8C9ffFTmstfjPPR84" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut.audience, ["https://example.us.auth0.com"]) + } + + func testMultipleAudiencesClaim() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiaHR0cHM6Ly9leGFtcGxlLnVzLmF1dGgw" + + "LmNvbSIsImh0dHBzOi8vYXBpLmV4YW1wbGUudXMuYXV0aDAuY29tIl19.sw24la9mmCmykudpyE-U1Ar5bbyuDMyKaW" + + "ksSkBXhrM" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut.audience, ["https://example.us.auth0.com", "https://api.example.us.auth0.com"]) + } + + func testIssuedAtClaim() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUudXMuYXV0aDAuY29t" + + "Iiwic3ViIjoiYXV0aDB8MTAxMDEwMTAxMCIsImF1ZCI6Imh0dHBzOi8vZXhhbXBsZS51cy5hdXRoMC5jb20iLCJleHAiOjE" + + "zNzI2NzQzMzYsImlhdCI6MTM3MjYzODMzNiwianRpIjoicXdlcnR5MTIzNDU2IiwibmJmIjoxMzcyNjM4MzM2fQ.PmTa620" + + "SkKEawqtY8sFsxesdnN8C9ffFTmstfjPPR84" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut.issuedAt, Date(timeIntervalSince1970: 1372638336)) + } + + func testNotBeforeClaim() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUudXMuYXV0aDAuY29t" + + "Iiwic3ViIjoiYXV0aDB8MTAxMDEwMTAxMCIsImF1ZCI6Imh0dHBzOi8vZXhhbXBsZS51cy5hdXRoMC5jb20iLCJleHAiOjE" + + "zNzI2NzQzMzYsImlhdCI6MTM3MjYzODMzNiwianRpIjoicXdlcnR5MTIzNDU2IiwibmJmIjoxMzcyNjM4MzM2fQ.PmTa620" + + "SkKEawqtY8sFsxesdnN8C9ffFTmstfjPPR84" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut.notBefore, Date(timeIntervalSince1970: 1372638336)) + } + + func testJWTIdClaim() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL2V4YW1wbGUudXMuYXV0aDAuY29t" + + "Iiwic3ViIjoiYXV0aDB8MTAxMDEwMTAxMCIsImF1ZCI6Imh0dHBzOi8vZXhhbXBsZS51cy5hdXRoMC5jb20iLCJleHAiOjE" + + "zNzI2NzQzMzYsImlhdCI6MTM3MjYzODMzNiwianRpIjoicXdlcnR5MTIzNDU2IiwibmJmIjoxMzcyNjM4MzM2fQ.PmTa620" + + "SkKEawqtY8sFsxesdnN8C9ffFTmstfjPPR84" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut.identifier, "qwerty123456") + } + + func testClaimByName() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13"]) + let claim = sut.claim(name: "custom_string_claim") + XCTAssertNotNil(claim.rawValue) + } + + func testCustomStringClaim() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13"]) + + let claim = sut.claim(name: "custom_string_claim") + XCTAssertEqual(claim.string, "Shawarma Friday!") + XCTAssertEqual(claim.array, ["Shawarma Friday!"]) + XCTAssertNil(claim.integer) + XCTAssertNil(claim.double) + XCTAssertNil(claim.date) + XCTAssertNil(claim.boolean) + } + + func testCustomIntegerClaim() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13"]) + let claim = sut["custom_integer_claim"] + XCTAssertNil(claim.string) + XCTAssertNil(claim.array) + XCTAssertEqual(claim.integer, 10) + XCTAssertEqual(claim.double, 10.0) + XCTAssertEqual(claim.date, Date(timeIntervalSince1970: 10)) + XCTAssertNil(claim.boolean) + } + + func testCustomIntegerClaim0() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13"]) + let claim = sut["custom_integer_claim_0"] + XCTAssertNil(claim.string) + XCTAssertNil(claim.array) + XCTAssertEqual(claim.integer, 0) + XCTAssertEqual(claim.double, 0.0) + XCTAssertEqual(claim.date, Date(timeIntervalSince1970: 0)) + XCTAssertNil(claim.boolean) + } + + func testCustomIntegerClaim1() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13"]) + let claim = sut["custom_integer_claim_1"] + XCTAssertNil(claim.string) + XCTAssertNil(claim.array) + XCTAssertEqual(claim.integer, 1) + XCTAssertEqual(claim.double, 1.0) + XCTAssertEqual(claim.date, Date(timeIntervalSince1970: 1)) + XCTAssertNil(claim.boolean) + } + + func testCustomIntegerClaimString() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13"]) + let claim = sut["custom_integer_claim_string"] + XCTAssertEqual(claim.string, "13") + XCTAssertEqual(claim.array, ["13"]) + XCTAssertEqual(claim.integer, 13) + XCTAssertEqual(claim.double, 13.0) + XCTAssertEqual(claim.date, Date(timeIntervalSince1970: 13)) + XCTAssertNil(claim.boolean) + } + + func testCustomDoubleClaim() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13", + "custom_double_claim": 3.1]) + let claim = sut["custom_double_claim"] + XCTAssertNil(claim.string) + XCTAssertNil(claim.array) + XCTAssertEqual(claim.integer, 3) + XCTAssertEqual(claim.double, 3.1) + XCTAssertEqual(claim.date, Date(timeIntervalSince1970: 3.1)) + XCTAssertNil(claim.boolean) + } + + func testCustomDoubleClaim0() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13", + "custom_double_claim_0.0": 0.0]) + let claim = sut["custom_double_claim_0.0"] + XCTAssertNil(claim.string) + XCTAssertNil(claim.array) + XCTAssertEqual(claim.integer, 0) + XCTAssertEqual(claim.double, 0.0) + XCTAssertEqual(claim.date, Date(timeIntervalSince1970: 0)) + XCTAssertNil(claim.boolean) + } + + func testCustomDoubleClaim1() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13", + "custom_double_claim_1.0": 1.0]) + let claim = sut["custom_double_claim_1.0"] + XCTAssertNil(claim.string) + XCTAssertNil(claim.array) + XCTAssertEqual(claim.integer, 1) + XCTAssertEqual(claim.double, 1.0) + XCTAssertEqual(claim.date, Date(timeIntervalSince1970: 1)) + XCTAssertNil(claim.boolean) + } + + func testCustomDoubleClaimString() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13", + "custom_double_claim_string": "1.3"]) + let claim = sut["custom_double_claim_string"] + XCTAssertEqual(claim.string, "1.3") + XCTAssertEqual(claim.array, ["1.3"]) + XCTAssertNil(claim.integer) + XCTAssertEqual(claim.double, 1.3) + XCTAssertEqual(claim.date, Date(timeIntervalSince1970: 1.3)) + XCTAssertNil(claim.boolean) + } + + func testCustomBooleanClaimTrue() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13", + "custom_boolean_claim_true": true]) + let claim = sut["custom_boolean_claim_true"] + XCTAssertNil(claim.string) + XCTAssertNil(claim.array) + XCTAssertNil(claim.integer) + XCTAssertNil(claim.double) + XCTAssertNil(claim.date) + XCTAssertEqual(claim.boolean, true) + } + + func testCustomBooleanClaimFalse() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13", + "custom_boolean_claim_false": false]) + let claim = sut["custom_boolean_claim_false"] + XCTAssertNil(claim.string) + XCTAssertNil(claim.array) + XCTAssertNil(claim.integer) + XCTAssertNil(claim.double) + XCTAssertNil(claim.date) + XCTAssertEqual(claim.boolean, false) + } + + func testMissingClaim() { + let sut = jwt(withBody: ["sub": UUID().uuidString, + "custom_string_claim": "Shawarma Friday!", + "custom_integer_claim": 10, + "custom_integer_claim_0": 0, + "custom_integer_claim_1": 1, + "custom_integer_claim_string": "13"]) + let unknownClaim = sut["missing_claim"] + XCTAssertNil(unknownClaim.array) + XCTAssertNil(unknownClaim.string) + XCTAssertNil(unknownClaim.integer) + XCTAssertNil(unknownClaim.double) + XCTAssertNil(unknownClaim.date) + XCTAssertNil(unknownClaim.boolean) + } + + func testRawClaimEmail() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3NhbXBsZXMuYXV0aDAuY29tIiwic3ViIjoiYXV0aDB8MTAxMDEwMTAxMCIsImF1ZCI6Imh0dHBzOi8vc2FtcGxlcy5hdXRoMC5jb20iLCJleHAiOjEzNzI2NzQzMzYsImlhdCI6MTM3MjYzODMzNiwianRpIjoicXdlcnR5MTIzNDU2IiwibmJmIjoxMzcyNjM4MzM2LCJlbWFpbCI6InVzZXJAaG9zdC5jb20iLCJjdXN0b20iOlsxLDIsM119.JeMRyHLkcoiqGxd958B6PABKNvhOhIgw-kbjecmhR_E" + let sut = try! decode(jwt: jwtString) + XCTAssertEqual(sut["email"].string, "user@host.com") + } + + func testRawClaimArray() { + let jwtString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL3NhbXBsZXMuYXV0aDAuY29tIiwic3ViIjoiYXV0aDB8MTAxMDEwMTAxMCIsImF1ZCI6Imh0dHBzOi8vc2FtcGxlcy5hdXRoMC5jb20iLCJleHAiOjEzNzI2NzQzMzYsImlhdCI6MTM3MjYzODMzNiwianRpIjoicXdlcnR5MTIzNDU2IiwibmJmIjoxMzcyNjM4MzM2LCJlbWFpbCI6InVzZXJAaG9zdC5jb20iLCJjdXN0b20iOlsxLDIsM119.JeMRyHLkcoiqGxd958B6PABKNvhOhIgw-kbjecmhR_E" + let sut = try! decode(jwt: jwtString) + XCTAssertNotNil(sut["custom"].rawValue as? [Int]) } }