Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VAPID Token Documentation #32

Merged
merged 1 commit into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Please check the [releases](https://github.com/mochidev/swift-webpush/releases)
```swift
dependencies: [
.package(
url: "https://github.com/mochidev/swift-webPush.git",
url: "https://github.com/mochidev/swift-webpush.git",
.upToNextMinor(from: "0.2.1")
),
],
Expand Down Expand Up @@ -180,11 +180,15 @@ The `WebPushTesting` module can be used to obtain a mocked `WebPushManager` inst

## Specifications

- [RFC 7515 JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515)
- [RFC 7519 JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519)
- [RFC 8030 Generic Event Delivery Using HTTP Push](https://datatracker.ietf.org/doc/html/rfc8030)
- [RFC 8188 Encrypted Content-Encoding for HTTP](https://datatracker.ietf.org/doc/html/rfc8188)
- [RFC 8291 Message Encryption for Web Push](https://datatracker.ietf.org/doc/html/rfc8291)
- [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push](https://datatracker.ietf.org/doc/html/rfc8292)

- [Push API Working Draft](https://www.w3.org/TR/push-api/)

## Contributing

Contribution is welcome! Please take a look at the issues already available, or start a new discussion to propose a new feature. Although guarantees can't be made regarding feature requests, PRs that fit within the goals of the project and that have been discussed beforehand are more than welcome!
Expand Down
6 changes: 5 additions & 1 deletion Sources/WebPush/VAPID/VAPIDKey.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ extension VAPID.Key: Identifiable {
///
/// This value can be shared as is with a subscription registration as the `applicationServerKey` key in JavaScript.
///
/// - SeeAlso: [Push API Working Draft §7.2. PushSubscriptionOptions Interface](https://www.w3.org/TR/push-api/#pushsubscriptionoptions-interface)
/// - SeeAlso: [Push API Working Draft §7.2. `PushSubscriptionOptions` Interface](https://www.w3.org/TR/push-api/#pushsubscriptionoptions-interface)
public struct ID: Hashable, Comparable, Codable, Sendable, CustomStringConvertible {
private var rawValue: String

Expand All @@ -93,6 +93,10 @@ extension VAPID.Key: Identifiable {
}
}

/// The public key component in a format suitable for user agents to consume.
///
/// - SeeAlso: [Push API Working Draft §7.2. `PushSubscriptionOptions` Interface](https://www.w3.org/TR/push-api/#dom-pushsubscriptionoptions-applicationserverkey)
/// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §3.2. Public Key Parameter ("k")](https://datatracker.ietf.org/doc/html/rfc8292#section-3.2)
public var id: ID {
ID(privateKey.publicKey.x963Representation.base64URLEncodedString())
}
Expand Down
37 changes: 33 additions & 4 deletions Sources/WebPush/VAPID/VAPIDToken.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,62 @@ extension VAPID {
/// An internal representation the token and authorization headers used self-identification.
///
/// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2)
/// - SeeAlso: [RFC 7515 JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515)
///- SeeAlso: [RFC 7519 JSON Web Token (JWT)](https://datatracker.ietf.org/doc/html/rfc7519)
struct Token: Hashable, Codable, Sendable {
enum CodingKeys: String, CodingKey {
case audience = "aud"
case subject = "sub"
case expiration = "exp"
}

/// The audience claim, which encodes the origin of the ``Subscriber/endpoint``
///
/// - SeeAlso: ``/Foundation/URL/origin``
/// - SeeAlso: [RFC 7519 JSON Web Token (JWT) §4.1.3. "aud" (Audience) Claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.3)
/// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2)
var audience: String
var subject: VAPID.Configuration.ContactInformation

/// The subject claim, which encodes contact information for the application server.
///
/// - SeeAlso: [RFC 7519 JSON Web Token (JWT) §4.1.2. "sub" (Subject) Claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.2)
/// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2.1. Application Server Contact Information](https://datatracker.ietf.org/doc/html/rfc8292#section-2.1)
var subject: Configuration.ContactInformation

/// The expiry claim, which encodes the number of seconds after 1970/01/01 when the token expires.
///
/// - SeeAlso: [RFC 7519 JSON Web Token (JWT) §4.1.4. "exp" (Expiration Time) Claim](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.4)
/// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2)
var expiration: Int

/// The standard header including the type and algorithm.
///
/// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §2. Application Server Self-Identification](https://datatracker.ietf.org/doc/html/rfc8292#section-2)
static let jwtHeader = Array(#"{"typ":"JWT","alg":"ES256"}"#.utf8).base64URLEncodedString()

/// Initialize a token with the specified claims.
init(
origin: String,
contactInformation: VAPID.Configuration.ContactInformation,
contactInformation: Configuration.ContactInformation,
expiration: Date
) {
self.audience = origin
self.subject = contactInformation
self.expiration = Int(expiration.timeIntervalSince1970)
}

/// Initialize a token with the specified claims.
init(
origin: String,
contactInformation: VAPID.Configuration.ContactInformation,
expiresIn: VAPID.Configuration.Duration
contactInformation: Configuration.ContactInformation,
expiresIn: Configuration.Duration
) {
audience = origin
subject = contactInformation
expiration = Int(Date.now.timeIntervalSince1970) + expiresIn.seconds
}

/// Initialize a token from a VAPID `Authorization` header's values.
init?(token: String, key: String) {
let components = token.split(separator: ".")

Expand All @@ -69,6 +92,7 @@ extension VAPID {
self = token
}

/// - SeeAlso: [RFC 7515 JSON Web Signature (JWS) §3. JSON Web Signature (JWS) Overview](https://datatracker.ietf.org/doc/html/rfc7515#section-3)
func generateJWT(signedBy signingKey: some VAPIDKeyProtocol) throws -> String {
let header = Self.jwtHeader

Expand All @@ -81,6 +105,9 @@ extension VAPID {
return "\(message).\(signature)"
}

/// Generate an `Authorization` header.
///
/// - SeeAlso: [RFC 8292 Voluntary Application Server Identification (VAPID) for Web Push §3. VAPID Authentication Scheme](https://datatracker.ietf.org/doc/html/rfc8292#section-3)
func generateAuthorization(signedBy signingKey: some VAPIDKeyProtocol) throws -> String {
let token = try generateJWT(signedBy: signingKey)
let key = signingKey.id
Expand All @@ -93,5 +120,7 @@ extension VAPID {
protocol VAPIDKeyProtocol: Identifiable, Sendable {
associatedtype Signature: ContiguousBytes

/// Returns a JWS signature for the message.
/// - SeeAlso: [RFC 7515 JSON Web Signature (JWS)](https://datatracker.ietf.org/doc/html/rfc7515)
func signature(for message: some DataProtocol) throws -> Signature
}
Loading