generated from TBD54566975/tbd-project-template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JWT signatures are required for accessing authenticated tbDEX endpoints. Currently, there isn't a need for Swift to verify a JWT, as that is done on the server side only, so that is not implemented yet.
- Loading branch information
Showing
5 changed files
with
173 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import Foundation | ||
|
||
/// Wrapper used to easily encode a `Date` to and decode a `Date` from an ISO 8601 formatted date string. | ||
@propertyWrapper | ||
struct ISO8601Date: Codable { | ||
var wrappedValue: Date? | ||
|
||
init(wrappedValue: Date?) { | ||
self.wrappedValue = wrappedValue | ||
} | ||
|
||
init(from decoder: Decoder) throws { | ||
let value = try decoder.singleValueContainer() | ||
let stringValue = try value.decode(String.self) | ||
if let date = ISO8601DateFormatter().date(from: stringValue) { | ||
wrappedValue = date | ||
} else { | ||
throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: [], debugDescription: "Failed to decode ISO Date. Invalid string format.")) | ||
} | ||
} | ||
|
||
func encode(to encoder: Encoder) throws { | ||
var container = encoder.singleValueContainer() | ||
|
||
if let wrappedValue { | ||
let string = ISO8601DateFormatter().string(from: wrappedValue) | ||
try container.encode(string) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import Foundation | ||
|
||
public struct JWT { | ||
|
||
/// Claims represents JWT (JSON Web Token) Claims | ||
/// | ||
/// See [RFC7519](https://tools.ietf.org/html/rfc7519#section-4) for more information | ||
public struct Claims: Codable { | ||
/// The "iss" (issuer) claim identifies the principal that issued the JWT. | ||
/// | ||
/// [Spec](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.1) | ||
let issuer: String? | ||
|
||
/// The "sub" (subject) claim identifies the principal that is the subject of the JWT. | ||
/// | ||
/// [Spec](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2) | ||
let subject: String? | ||
|
||
/// The "aud" (audience) claim identifies the recipients that the JWT is intended for. | ||
/// | ||
/// [Spec](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3) | ||
let audience: String? | ||
|
||
/// The "exp" (expiration time) claim identifies the expiration time on | ||
/// or after which the JWT must not be accepted for processing. | ||
/// | ||
/// [Spec](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4) | ||
@ISO8601Date private(set) var expiration: Date? | ||
|
||
/// The "nbf" (not before) claim identifies the time before which the JWT | ||
/// must not be accepted for processing. | ||
/// | ||
/// [Spec](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5) | ||
@ISO8601Date private(set) var notBefore: Date? | ||
|
||
/// The "iat" (issued at) claim identifies the time at which the JWT was issued. | ||
/// | ||
/// [Spec](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.6) | ||
@ISO8601Date private(set) var issuedAt: Date? | ||
|
||
/// The "jti" (JWT ID) claim provides a unique identifier for the JWT. | ||
/// | ||
/// [Spec](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.7) | ||
let jwtID: String? | ||
|
||
// Default Initializer | ||
public init( | ||
issuer: String? = nil, | ||
subject: String? = nil, | ||
audience: String? = nil, | ||
expiration: Date? = nil, | ||
notBefore: Date? = nil, | ||
issuedAt: Date? = nil, | ||
jwtID: String? = nil | ||
) { | ||
self.issuer = issuer | ||
self.subject = subject | ||
self.audience = audience | ||
self.expiration = expiration | ||
self.notBefore = notBefore | ||
self.issuedAt = issuedAt | ||
self.jwtID = jwtID | ||
} | ||
|
||
enum CodingKeys: String, CodingKey { | ||
case issuer = "iss" | ||
case subject = "sub" | ||
case audience = "aud" | ||
case expiration = "exp" | ||
case notBefore = "nbf" | ||
case issuedAt = "iat" | ||
case jwtID = "jti" | ||
} | ||
} | ||
|
||
/// Signs the provied JWT claims with the provided BearerDID. | ||
/// - Parameters: | ||
/// - did: The BearerDID to sign the JWT with | ||
/// - claims: The claims to sign | ||
/// - Returns: The signed JWT | ||
public static func sign(did: BearerDID, claims: Claims) throws -> String { | ||
let payload = try JSONEncoder().encode(claims) | ||
|
||
return try JWS.sign( | ||
did: did, | ||
payload: payload, | ||
options: .init( | ||
detached: false, | ||
type: "JWT" | ||
) | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import XCTest | ||
|
||
@testable import Web5 | ||
|
||
final class JWTTests: XCTestCase { | ||
|
||
func test_sign() throws { | ||
let did = try DIDJWK.create(keyManager: InMemoryKeyManager()) | ||
|
||
let claims = JWT.Claims(issuer: did.identifier) | ||
let jwt = try JWT.sign(did: did, claims: claims) | ||
|
||
XCTAssertFalse(jwt.isEmpty) | ||
} | ||
} |