Skip to content

Commit

Permalink
Initial provider token fixes. (#49)
Browse files Browse the repository at this point in the history
* Initial provider token fixes.

* Fix tests.

* UPdate for API Breakage.

* Updates to bearertoken

* Cleanup tests.

* Revert Old deprecated APNSwiftBearer to its original form

* Remove nio.

* Add deprecration.

* Remove old signature.

* Add private initializier method.

* Update to convinience init.

* Update license header.
  • Loading branch information
kylebrowning committed Sep 24, 2019
1 parent 0699bb1 commit 46e602a
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 46 deletions.
2 changes: 1 addition & 1 deletion Sources/APNSwift/APNSSwiftJWT/APNSwiftJWT.swift
Expand Up @@ -47,7 +47,7 @@ internal struct APNSwiftJWT: Codable {

private let payload: Payload

internal init(keyID: String, teamID: String, issueDate: Date, expireDuration _: TimeInterval) {
internal init(keyID: String, teamID: String, issueDate: Date) {
header = Header(keyID: keyID)
let iat = Int(issueDate.timeIntervalSince1970.rounded())
payload = Payload(teamID: teamID, issueDate: iat)
Expand Down
3 changes: 2 additions & 1 deletion Sources/APNSwift/APNSwiftBearerToken.swift
Expand Up @@ -14,6 +14,7 @@

import Foundation

@available(*, deprecated, message: "Bearer Tokens are handled internally now, and no longer exposed.")
public struct APNSwiftBearerToken {
let configuration: APNSwiftConfiguration
let timeout: TimeInterval
Expand All @@ -38,7 +39,7 @@ public struct APNSwiftBearerToken {
}

private func createToken() throws -> String {
let jwt = APNSwiftJWT(keyID: configuration.keyIdentifier, teamID: configuration.teamIdentifier, issueDate: Date(), expireDuration: timeout)
let jwt = APNSwiftJWT(keyID: configuration.keyIdentifier, teamID: configuration.teamIdentifier, issueDate: Date())
var token: String
let digestValues = try jwt.getDigest()
var signature = try configuration.signer.sign(digest: digestValues.fixedDigest)
Expand Down
52 changes: 52 additions & 0 deletions Sources/APNSwift/APNSwiftBearerTokenFactory.swift
@@ -0,0 +1,52 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the APNSwift open source project
//
// Copyright (c) 2019 the APNSwift project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of APNSwift project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//


import Foundation
import NIO
internal final class APNSwiftBearerTokenFactory {
var updateTask: RepeatedTask?
let eventLoop: EventLoop
var currentBearerToken: String
var cancelled = false
let configuration: APNSwiftConfiguration

init(eventLoop: EventLoop, configuration: APNSwiftConfiguration) throws {
self.eventLoop = eventLoop
self.eventLoop.assertInEventLoop()
self.configuration = configuration
self.currentBearerToken = try APNSwiftBearerTokenFactory.makeNewBearerToken(configuration: configuration)
self.updateTask = eventLoop.scheduleRepeatedTask(initialDelay: .minutes(55), delay: .minutes(55)) { task in
self.currentBearerToken = try APNSwiftBearerTokenFactory.makeNewBearerToken(configuration: configuration)
}
}

func cancel() {
self.eventLoop.assertInEventLoop()
self.cancelled = true
self.updateTask?.cancel()
self.updateTask = nil
}

deinit {
assert(self.cancelled, "APNSwiftBearerTokenFactory not closed on deinit. You must call APNSwiftBearerTokenFactory.close when you no longer need it to make sure the library can discard the resources")
}
static func makeNewBearerToken(configuration: APNSwiftConfiguration) throws -> String {
let jwt = APNSwiftJWT(keyID: configuration.keyIdentifier, teamID: configuration.teamIdentifier, issueDate: Date())
let digestValues = try jwt.getDigest()
var signature = try configuration.signer.sign(digest: digestValues.fixedDigest)
let data = signature.readData(length: signature.readableBytes)!
return digestValues.digest + "." + data.base64EncodedURLString()
}
}
37 changes: 30 additions & 7 deletions Sources/APNSwift/APNSwiftConnection.swift
Expand Up @@ -85,7 +85,6 @@ public final class APNSwiftConnection {

let sslContext = try! NIOSSLContext(configuration: configuration.tlsConfiguration)
let connectionFullyUpPromise = eventLoop.makePromise(of: Void.self)

let tcpConnection = ClientBootstrap(group: eventLoop).connect(host: configuration.url.host!, port: 443)
tcpConnection.cascadeFailure(to: connectionFullyUpPromise)
return tcpConnection.flatMap { channel in
Expand All @@ -96,8 +95,16 @@ public final class APNSwiftConnection {
channel.configureHTTP2Pipeline(mode: .client) { channel, _ in
return channel.eventLoop.makeFailedFuture(UnsupportedServerPushError())
}.flatMap { multiplexer in
connectionFullyUpPromise.futureResult.map {
return APNSwiftConnection(channel: channel, multiplexer: multiplexer, configuration: configuration)
var tokenFactory: APNSwiftBearerTokenFactory? = nil
if configuration.tlsConfiguration.privateKey == nil {
do {
tokenFactory = try APNSwiftBearerTokenFactory(eventLoop: eventLoop, configuration: configuration)
} catch {
return channel.eventLoop.makeFailedFuture(APNSwiftError.SigningError.invalidSignatureData)
}
}
return connectionFullyUpPromise.futureResult.map { () -> APNSwiftConnection in
return APNSwiftConnection(channel: channel, multiplexer: multiplexer, configuration: configuration, bearerTokenFactory: tokenFactory)
}
}
}
Expand All @@ -107,11 +114,22 @@ public final class APNSwiftConnection {
public let multiplexer: HTTP2StreamMultiplexer
public let channel: Channel
public let configuration: APNSwiftConfiguration
private var bearerTokenFactory: APNSwiftBearerTokenFactory?

public init(channel: Channel, multiplexer: HTTP2StreamMultiplexer, configuration: APNSwiftConfiguration) {
private init(channel: Channel, multiplexer: HTTP2StreamMultiplexer, configuration: APNSwiftConfiguration, bearerTokenFactory: APNSwiftBearerTokenFactory?) {
self.channel = channel
self.multiplexer = multiplexer
self.configuration = configuration
self.bearerTokenFactory = bearerTokenFactory
}

@available(*, deprecated, message: "APNSwiftConnection is initialized internally now.")
public convenience init(channel: Channel, multiplexer: HTTP2StreamMultiplexer, configuration: APNSwiftConfiguration) {
var tokenFactory: APNSwiftBearerTokenFactory? = nil
if configuration.tlsConfiguration.privateKey == nil {
tokenFactory = try? APNSwiftBearerTokenFactory(eventLoop: channel.eventLoop, configuration: configuration)
}
self.init(channel: channel, multiplexer: multiplexer, configuration: configuration, bearerTokenFactory: tokenFactory)
}

/**
Expand All @@ -135,12 +153,12 @@ public final class APNSwiftConnection {
try apns.send(notification, bearerToken: bearerToken,to: "b27a07be2092c7fbb02ab5f62f3135c615e18acc0ddf39a30ffde34d41665276", with: JSONEncoder(), expiration: expiry, priority: 10, collapseIdentifier: "huro2").wait()
```
*/
public func send<Notification: APNSwiftNotification>(_ notification: Notification, pushType: APNSwiftConnection.PushType, bearerToken: APNSwiftBearerToken, to deviceToken: String, with encoder: JSONEncoder = JSONEncoder(), expiration: Date? = nil, priority: Int? = nil, collapseIdentifier: String? = nil, topic: String? = nil) -> EventLoopFuture<Void> {
public func send<Notification: APNSwiftNotification>(_ notification: Notification, pushType: APNSwiftConnection.PushType, to deviceToken: String, with encoder: JSONEncoder = JSONEncoder(), expiration: Date? = nil, priority: Int? = nil, collapseIdentifier: String? = nil, topic: String? = nil) -> EventLoopFuture<Void> {
let streamPromise = channel.eventLoop.makePromise(of: Channel.self)
multiplexer.createStreamChannel(promise: streamPromise) { channel, streamID in
let handlers: [ChannelHandler] = [
HTTP2ToHTTP1ClientCodec(streamID: streamID, httpProtocol: .https),
APNSwiftRequestEncoder(deviceToken: deviceToken, configuration: self.configuration, bearerToken: bearerToken, pushType: pushType, expiration: expiration, priority: priority, collapseIdentifier: collapseIdentifier, topic: topic),
APNSwiftRequestEncoder(deviceToken: deviceToken, configuration: self.configuration, bearerToken: self.bearerTokenFactory?.currentBearerToken, pushType: pushType, expiration: expiration, priority: priority, collapseIdentifier: collapseIdentifier, topic: topic),
APNSwiftResponseDecoder(),
APNSwiftStreamHandler(),
]
Expand All @@ -163,15 +181,20 @@ public final class APNSwiftConnection {
responsePromise.futureResult
}
}
@available(*, deprecated, message: "Bearer Tokens are handled internally now, and no longer exposed.")
public func send<Notification: APNSwiftNotification>(_ notification: Notification, bearerToken: APNSwiftBearerToken, to deviceToken: String, with encoder: JSONEncoder = JSONEncoder(), expiration: Date? = nil, priority: Int? = nil, collapseIdentifier: String? = nil, topic: String? = nil) -> EventLoopFuture<Void> {
return self.send(notification, pushType: .alert, bearerToken: bearerToken, to: deviceToken, with: encoder, expiration: expiration, priority: priority, collapseIdentifier: collapseIdentifier, topic: topic)
return self.send(notification, pushType: .alert, to: deviceToken, with: encoder, expiration: expiration, priority: priority, collapseIdentifier: collapseIdentifier, topic: topic)
}

var onClose: EventLoopFuture<Void> {
return channel.closeFuture
}

public func close() -> EventLoopFuture<Void> {
channel.eventLoop.execute {
self.bearerTokenFactory?.cancel()
self.bearerTokenFactory = nil
}
return channel.close(mode: .all)
}
}
16 changes: 6 additions & 10 deletions Sources/APNSwift/APNSwiftRequestEncoder.swift
Expand Up @@ -26,7 +26,7 @@ internal final class APNSwiftRequestEncoder: ChannelOutboundHandler {
typealias OutboundOut = HTTPClientRequestPart

let configuration: APNSwiftConfiguration
var bearerToken: APNSwiftBearerToken
var bearerToken: String?
let deviceToken: String
let priority: Int?
let expiration: Date?
Expand All @@ -35,7 +35,7 @@ internal final class APNSwiftRequestEncoder: ChannelOutboundHandler {
let pushType: APNSwiftConnection.PushType?


init(deviceToken: String, configuration: APNSwiftConfiguration, bearerToken: APNSwiftBearerToken, pushType: APNSwiftConnection.PushType, expiration: Date?, priority: Int?, collapseIdentifier: String?, topic: String?) {
init(deviceToken: String, configuration: APNSwiftConfiguration, bearerToken: String?, pushType: APNSwiftConnection.PushType, expiration: Date?, priority: Int?, collapseIdentifier: String?, topic: String?) {
self.configuration = configuration
self.bearerToken = bearerToken
self.deviceToken = deviceToken
Expand All @@ -46,7 +46,7 @@ internal final class APNSwiftRequestEncoder: ChannelOutboundHandler {
self.pushType = pushType
}

convenience init(deviceToken: String, configuration: APNSwiftConfiguration, bearerToken: APNSwiftBearerToken, expiration: Date?, priority: Int?, collapseIdentifier: String?, topic: String? = nil) {
convenience init(deviceToken: String, configuration: APNSwiftConfiguration, bearerToken: String, expiration: Date?, priority: Int?, collapseIdentifier: String?, topic: String? = nil) {
self.init(deviceToken: deviceToken, configuration: configuration, bearerToken: bearerToken, pushType: .alert, expiration: expiration, priority: priority, collapseIdentifier: collapseIdentifier, topic: topic)

}
Expand Down Expand Up @@ -77,13 +77,9 @@ internal final class APNSwiftRequestEncoder: ChannelOutboundHandler {
reqHead.headers.add(name: "apns-push-type", value: pushType.rawValue)
}
reqHead.headers.add(name: "host", value: configuration.url.host!)
// Only use token auth if private key is nil
if configuration.tlsConfiguration.privateKey == nil {
guard let token = bearerToken.token else {
promise?.fail(APNSwiftError.SigningError.invalidSignatureData)
return
}
reqHead.headers.add(name: "authorization", value: "bearer \(token)")
// Only use token auth if bearer token is present.
if let bearerToken = self.bearerToken {
reqHead.headers.add(name: "authorization", value: "bearer \(bearerToken)")
}

context.write(wrapOutboundOut(.head(reqHead))).cascadeFailure(to: promise)
Expand Down
8 changes: 6 additions & 2 deletions Sources/APNSwiftExample/main.swift
Expand Up @@ -52,11 +52,15 @@ let aps = APNSwiftPayload(alert: alert, badge: 0, sound: .critical(apsSound), ha
let temp = try! JSONEncoder().encode(aps)
let string = String(bytes: temp, encoding: .utf8)
let notification = AcmeNotification(acme2: ["bang", "whiz"], aps: aps)
let token = APNSwiftBearerToken(configuration: apnsConfig, timeout: 50.0)

do {
let expiry = Date().addingTimeInterval(5)
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
for _ in 1...5 {
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
}
} catch {
print(error)
}
Expand Down
13 changes: 6 additions & 7 deletions Sources/APNSwiftPemExample/main.swift
Expand Up @@ -57,16 +57,15 @@ let aps = APNSwiftPayload(alert: alert, badge: 0, sound: .critical(apsSound), ha
let temp = try! JSONEncoder().encode(aps)
let string = String(bytes: temp, encoding: .utf8)
let notification = AcmeNotification(acme2: ["bang", "whiz"], aps: aps)
let token = APNSwiftBearerToken(configuration: apnsConfig, timeout: 50.0)

do {
let expiry = Date().addingTimeInterval(5)
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
for _ in 1...5 {
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
}
} catch {
print(error)
}
Expand Down
4 changes: 2 additions & 2 deletions Tests/APNSwiftJWTTests/JWTTests.swift
Expand Up @@ -31,7 +31,7 @@ final class JWTTests: XCTestCase {
let teamID = "8RX5AF8F6Z"
let keyID = "9N8238KQ6Z"
let date = Date()
let jwt = APNSwiftJWT(keyID: keyID, teamID: teamID, issueDate: date, expireDuration: 10.0)
let jwt = APNSwiftJWT(keyID: keyID, teamID: teamID, issueDate: date)
let token = try jwt.getDigest()

let part = token.digest.split(separator: ".")
Expand Down Expand Up @@ -59,7 +59,7 @@ final class JWTTests: XCTestCase {
let teamID = "8RX5AF8F6Z"
let keyID = "9N8238KQ6Z"
let date = Date()
let jwt = APNSwiftJWT(keyID: keyID, teamID: teamID, issueDate: date, expireDuration: 10.0)
let jwt = APNSwiftJWT(keyID: keyID, teamID: teamID, issueDate: date)

let privateKey = """
-----BEGIN EC PRIVATE KEY-----
Expand Down
2 changes: 1 addition & 1 deletion Tests/APNSwiftTests/APNSwiftConfigurationTests.swift
Expand Up @@ -52,7 +52,7 @@ class APNSwiftConfigurationTests: XCTestCase {
let teamID = "8RX5AF8F6Z"
let keyID = "9N8238KQ6Z"
let date = Date()
let jwt = APNSwiftJWT(keyID: keyID, teamID: teamID, issueDate: date, expireDuration: 10.0)
let jwt = APNSwiftJWT(keyID: keyID, teamID: teamID, issueDate: date)
let digestValues = try jwt.getDigest()
let _ = try signer.sign(digest: digestValues.fixedDigest)

Expand Down

0 comments on commit 46e602a

Please sign in to comment.