Skip to content

Commit

Permalink
feat(jwt): add some useful methods to JWT so it can provide the essen…
Browse files Browse the repository at this point in the history
…tial information
  • Loading branch information
beatt83 committed Mar 28, 2024
1 parent 46246c5 commit 071aa57
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 9 deletions.
33 changes: 24 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,21 @@ import JSONWebToken

You can access [here](https://beatt83.github.io/jose-swift/documentation/jose_swift/) to the documentation.

For more examples on how to use this library please try to check the unit tests, they are extensive and should provide more information.

## Modules

### JWK (JSON Web Key)
JWK is a standard way to represent cryptographic keys in a JSON format, as defined in [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517). This module provides functionalities for generating, parsing, and managing JWKs, which are essential for encryption, decryption, and signing processes.

```swift
let keyJWK = JWK(keyType: .rsa, algorithm: "A256GCM", keyID: rsaKeyId, e: rsaKeyExponent, n: rsaKeyModulus)
// ---------------------
let key = secp256k1.Signing.PrivateKey()
let keyJWK = key.jwkRepresentation
// ---------------------
let key = Curve25519.KeyAgreement.PrivateKey()
let publicKeyJWK = key.jwkRepresentation.publicKey
```

### JWS (JSON Web Signature)
Expand All @@ -252,7 +260,8 @@ Example:

```swift
let payload = "Hello world".data(using: .utf8)!
let keyJWK = ... //JWK key
let key = secp256k1.Signing.PrivateKey()
let keyJWK = key.jwkRepresentation

let jws = try JWS(payload: payload, key: keyJWK)

Expand Down Expand Up @@ -298,6 +307,9 @@ JWE represents encrypted content using JSON-based data structures, following the
- A128GCM (AES GCM using 128-bit key)
- A192GCM (AES GCM using 192-bit key)
- A256GCM (AES GCM using 256-bit key)

3. **Compression Algorithms**:
- DEFLATE (zip)

Example:

Expand All @@ -310,6 +322,7 @@ let serialization = try JWE(
payload: payload,
keyManagementAlg: .a256KW,
encryptionAlgorithm: .a256GCM,
compressionAlgorithm: .zip,
recipientKey: keyJWK
)

Expand Down Expand Up @@ -362,11 +375,12 @@ Example:
- Signed JWT

```swift
let keyJWK = ... //JWK key
let key = P256.Signing.PrivateKey()
let keyJWK = key.jwkRepresentation
let mockClaims = DefaultJWTClaims(
issuer: "testAlice",
subject: "Alice",
expirationTime: expiredAt
iss: "testAlice",
sub: "Alice",
exp: expiredAt
)

let jwt = try JWT.signed(
Expand All @@ -384,11 +398,12 @@ let verifiedPayload = verifiedJWT.payload
- Encrypted JWT

```swift
let keyJWK = ... //JWK key
let key = Curve25519.KeyAgreement.PrivateKey()
let keyJWK = key.jwkRepresentation
let mockClaims = DefaultJWTClaims(
issuer: "testAlice",
subject: "Alice",
expirationTime: expiredAt
iss: "testAlice",
sub: "Alice",
exp: expiredAt
)

let jwt = try JWT.encrypt(
Expand Down
6 changes: 6 additions & 0 deletions Sources/JSONWebToken/JWT+Error.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,11 @@ extension JWT {

/// Error indicating a mismatch between the expected audience (`aud` claim) and the actual audience of the JWT.
case audienceMismatch

// Error indicating that JWT formats supported are JWS strings and JWE strings
case unsupportedFormat

// Error indicating that JWT of JWE format cannot retrieve payload without decryption
case cannotRetrievePayloadFromJWE
}
}
81 changes: 81 additions & 0 deletions Sources/JSONWebToken/JWT.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,85 @@ public struct JWT<C: JWTRegisteredFieldsClaims> {
return jws.compactSerialization
}
}

public init(payload: C, format: Format) {
self.payload = payload
self.format = format
}

public init(jwtString: String) throws {
self.payload = try Self.getPayload(jwtString: jwtString)
self.format = try Self.jwtFormat(jwtString: jwtString)
}
}

public extension JWT {
static func getPayload<Payload: JWTRegisteredFieldsClaims>(jwtString: String) throws -> Payload {
return try JSONDecoder().decode(Payload.self, from: getPayload(jwtString: jwtString))
}

static func getPayload(jwtString: String) throws -> Data {
switch try jwtFormat(jwtString: jwtString) {
case .jwe:
throw JWTError.cannotRetrievePayloadFromJWE
case .jws(let jws):
return jws.payload
}
}

static func getIssuer(jwtString: String) throws -> String? {
let payload: DefaultJWTClaimsImpl = try getPayload(jwtString: jwtString)
return payload.iss
}

static func getSubject(jwtString: String) throws -> String? {
let payload: DefaultJWTClaimsImpl = try getPayload(jwtString: jwtString)
return payload.sub
}

static func getNotBeforeTime(jwtString: String) throws -> Date? {
let payload: DefaultJWTClaimsImpl = try getPayload(jwtString: jwtString)
return payload.nbf
}

static func getExpirationTime(jwtString: String) throws -> Date? {
let payload: DefaultJWTClaimsImpl = try getPayload(jwtString: jwtString)
return payload.exp
}

static func getIssuedAt(jwtString: String) throws -> Date? {
let payload: DefaultJWTClaimsImpl = try getPayload(jwtString: jwtString)
return payload.iat
}

static func getID(jwtString: String) throws -> String? {
let payload: DefaultJWTClaimsImpl = try getPayload(jwtString: jwtString)
return payload.jti
}

static func getAudience(jwtString: String) throws -> [String]? {
let payload: DefaultJWTClaimsImpl = try getPayload(jwtString: jwtString)
return payload.aud
}

static func getHeader(jwtString: String) throws -> Data {
switch try jwtFormat(jwtString: jwtString) {
case .jwe(let jwe):
return jwe.protectedHeaderData
case .jws(let jws):
return jws.protectedHeaderData
}
}

static func jwtFormat(jwtString: String) throws -> Format {
let components = jwtString.components(separatedBy: ".")
switch components.count {
case 3:
return try .jws(.init(jwsString: jwtString))
case 5:
return try .jwe(.init(compactString: jwtString))
default:
throw JWTError.somethingWentWrong
}
}
}

0 comments on commit 071aa57

Please sign in to comment.