Skip to content

Commit 46e602a

Browse files
authored
Initial provider token fixes. (#49)
* 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.
1 parent 0699bb1 commit 46e602a

File tree

10 files changed

+148
-46
lines changed

10 files changed

+148
-46
lines changed

Sources/APNSwift/APNSSwiftJWT/APNSwiftJWT.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ internal struct APNSwiftJWT: Codable {
4747

4848
private let payload: Payload
4949

50-
internal init(keyID: String, teamID: String, issueDate: Date, expireDuration _: TimeInterval) {
50+
internal init(keyID: String, teamID: String, issueDate: Date) {
5151
header = Header(keyID: keyID)
5252
let iat = Int(issueDate.timeIntervalSince1970.rounded())
5353
payload = Payload(teamID: teamID, issueDate: iat)

Sources/APNSwift/APNSwiftBearerToken.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import Foundation
1616

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

4041
private func createToken() throws -> String {
41-
let jwt = APNSwiftJWT(keyID: configuration.keyIdentifier, teamID: configuration.teamIdentifier, issueDate: Date(), expireDuration: timeout)
42+
let jwt = APNSwiftJWT(keyID: configuration.keyIdentifier, teamID: configuration.teamIdentifier, issueDate: Date())
4243
var token: String
4344
let digestValues = try jwt.getDigest()
4445
var signature = try configuration.signer.sign(digest: digestValues.fixedDigest)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the APNSwift open source project
4+
//
5+
// Copyright (c) 2019 the APNSwift project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of APNSwift project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
16+
import Foundation
17+
import NIO
18+
internal final class APNSwiftBearerTokenFactory {
19+
var updateTask: RepeatedTask?
20+
let eventLoop: EventLoop
21+
var currentBearerToken: String
22+
var cancelled = false
23+
let configuration: APNSwiftConfiguration
24+
25+
init(eventLoop: EventLoop, configuration: APNSwiftConfiguration) throws {
26+
self.eventLoop = eventLoop
27+
self.eventLoop.assertInEventLoop()
28+
self.configuration = configuration
29+
self.currentBearerToken = try APNSwiftBearerTokenFactory.makeNewBearerToken(configuration: configuration)
30+
self.updateTask = eventLoop.scheduleRepeatedTask(initialDelay: .minutes(55), delay: .minutes(55)) { task in
31+
self.currentBearerToken = try APNSwiftBearerTokenFactory.makeNewBearerToken(configuration: configuration)
32+
}
33+
}
34+
35+
func cancel() {
36+
self.eventLoop.assertInEventLoop()
37+
self.cancelled = true
38+
self.updateTask?.cancel()
39+
self.updateTask = nil
40+
}
41+
42+
deinit {
43+
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")
44+
}
45+
static func makeNewBearerToken(configuration: APNSwiftConfiguration) throws -> String {
46+
let jwt = APNSwiftJWT(keyID: configuration.keyIdentifier, teamID: configuration.teamIdentifier, issueDate: Date())
47+
let digestValues = try jwt.getDigest()
48+
var signature = try configuration.signer.sign(digest: digestValues.fixedDigest)
49+
let data = signature.readData(length: signature.readableBytes)!
50+
return digestValues.digest + "." + data.base64EncodedURLString()
51+
}
52+
}

Sources/APNSwift/APNSwiftConnection.swift

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ public final class APNSwiftConnection {
8585

8686
let sslContext = try! NIOSSLContext(configuration: configuration.tlsConfiguration)
8787
let connectionFullyUpPromise = eventLoop.makePromise(of: Void.self)
88-
8988
let tcpConnection = ClientBootstrap(group: eventLoop).connect(host: configuration.url.host!, port: 443)
9089
tcpConnection.cascadeFailure(to: connectionFullyUpPromise)
9190
return tcpConnection.flatMap { channel in
@@ -96,8 +95,16 @@ public final class APNSwiftConnection {
9695
channel.configureHTTP2Pipeline(mode: .client) { channel, _ in
9796
return channel.eventLoop.makeFailedFuture(UnsupportedServerPushError())
9897
}.flatMap { multiplexer in
99-
connectionFullyUpPromise.futureResult.map {
100-
return APNSwiftConnection(channel: channel, multiplexer: multiplexer, configuration: configuration)
98+
var tokenFactory: APNSwiftBearerTokenFactory? = nil
99+
if configuration.tlsConfiguration.privateKey == nil {
100+
do {
101+
tokenFactory = try APNSwiftBearerTokenFactory(eventLoop: eventLoop, configuration: configuration)
102+
} catch {
103+
return channel.eventLoop.makeFailedFuture(APNSwiftError.SigningError.invalidSignatureData)
104+
}
105+
}
106+
return connectionFullyUpPromise.futureResult.map { () -> APNSwiftConnection in
107+
return APNSwiftConnection(channel: channel, multiplexer: multiplexer, configuration: configuration, bearerTokenFactory: tokenFactory)
101108
}
102109
}
103110
}
@@ -107,11 +114,22 @@ public final class APNSwiftConnection {
107114
public let multiplexer: HTTP2StreamMultiplexer
108115
public let channel: Channel
109116
public let configuration: APNSwiftConfiguration
117+
private var bearerTokenFactory: APNSwiftBearerTokenFactory?
110118

111-
public init(channel: Channel, multiplexer: HTTP2StreamMultiplexer, configuration: APNSwiftConfiguration) {
119+
private init(channel: Channel, multiplexer: HTTP2StreamMultiplexer, configuration: APNSwiftConfiguration, bearerTokenFactory: APNSwiftBearerTokenFactory?) {
112120
self.channel = channel
113121
self.multiplexer = multiplexer
114122
self.configuration = configuration
123+
self.bearerTokenFactory = bearerTokenFactory
124+
}
125+
126+
@available(*, deprecated, message: "APNSwiftConnection is initialized internally now.")
127+
public convenience init(channel: Channel, multiplexer: HTTP2StreamMultiplexer, configuration: APNSwiftConfiguration) {
128+
var tokenFactory: APNSwiftBearerTokenFactory? = nil
129+
if configuration.tlsConfiguration.privateKey == nil {
130+
tokenFactory = try? APNSwiftBearerTokenFactory(eventLoop: channel.eventLoop, configuration: configuration)
131+
}
132+
self.init(channel: channel, multiplexer: multiplexer, configuration: configuration, bearerTokenFactory: tokenFactory)
115133
}
116134

117135
/**
@@ -135,12 +153,12 @@ public final class APNSwiftConnection {
135153
try apns.send(notification, bearerToken: bearerToken,to: "b27a07be2092c7fbb02ab5f62f3135c615e18acc0ddf39a30ffde34d41665276", with: JSONEncoder(), expiration: expiry, priority: 10, collapseIdentifier: "huro2").wait()
136154
```
137155
*/
138-
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> {
156+
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> {
139157
let streamPromise = channel.eventLoop.makePromise(of: Channel.self)
140158
multiplexer.createStreamChannel(promise: streamPromise) { channel, streamID in
141159
let handlers: [ChannelHandler] = [
142160
HTTP2ToHTTP1ClientCodec(streamID: streamID, httpProtocol: .https),
143-
APNSwiftRequestEncoder(deviceToken: deviceToken, configuration: self.configuration, bearerToken: bearerToken, pushType: pushType, expiration: expiration, priority: priority, collapseIdentifier: collapseIdentifier, topic: topic),
161+
APNSwiftRequestEncoder(deviceToken: deviceToken, configuration: self.configuration, bearerToken: self.bearerTokenFactory?.currentBearerToken, pushType: pushType, expiration: expiration, priority: priority, collapseIdentifier: collapseIdentifier, topic: topic),
144162
APNSwiftResponseDecoder(),
145163
APNSwiftStreamHandler(),
146164
]
@@ -163,15 +181,20 @@ public final class APNSwiftConnection {
163181
responsePromise.futureResult
164182
}
165183
}
184+
@available(*, deprecated, message: "Bearer Tokens are handled internally now, and no longer exposed.")
166185
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> {
167-
return self.send(notification, pushType: .alert, bearerToken: bearerToken, to: deviceToken, with: encoder, expiration: expiration, priority: priority, collapseIdentifier: collapseIdentifier, topic: topic)
186+
return self.send(notification, pushType: .alert, to: deviceToken, with: encoder, expiration: expiration, priority: priority, collapseIdentifier: collapseIdentifier, topic: topic)
168187
}
169188

170189
var onClose: EventLoopFuture<Void> {
171190
return channel.closeFuture
172191
}
173192

174193
public func close() -> EventLoopFuture<Void> {
194+
channel.eventLoop.execute {
195+
self.bearerTokenFactory?.cancel()
196+
self.bearerTokenFactory = nil
197+
}
175198
return channel.close(mode: .all)
176199
}
177200
}

Sources/APNSwift/APNSwiftRequestEncoder.swift

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ internal final class APNSwiftRequestEncoder: ChannelOutboundHandler {
2626
typealias OutboundOut = HTTPClientRequestPart
2727

2828
let configuration: APNSwiftConfiguration
29-
var bearerToken: APNSwiftBearerToken
29+
var bearerToken: String?
3030
let deviceToken: String
3131
let priority: Int?
3232
let expiration: Date?
@@ -35,7 +35,7 @@ internal final class APNSwiftRequestEncoder: ChannelOutboundHandler {
3535
let pushType: APNSwiftConnection.PushType?
3636

3737

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

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

5252
}
@@ -77,13 +77,9 @@ internal final class APNSwiftRequestEncoder: ChannelOutboundHandler {
7777
reqHead.headers.add(name: "apns-push-type", value: pushType.rawValue)
7878
}
7979
reqHead.headers.add(name: "host", value: configuration.url.host!)
80-
// Only use token auth if private key is nil
81-
if configuration.tlsConfiguration.privateKey == nil {
82-
guard let token = bearerToken.token else {
83-
promise?.fail(APNSwiftError.SigningError.invalidSignatureData)
84-
return
85-
}
86-
reqHead.headers.add(name: "authorization", value: "bearer \(token)")
80+
// Only use token auth if bearer token is present.
81+
if let bearerToken = self.bearerToken {
82+
reqHead.headers.add(name: "authorization", value: "bearer \(bearerToken)")
8783
}
8884

8985
context.write(wrapOutboundOut(.head(reqHead))).cascadeFailure(to: promise)

Sources/APNSwiftExample/main.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,15 @@ let aps = APNSwiftPayload(alert: alert, badge: 0, sound: .critical(apsSound), ha
5252
let temp = try! JSONEncoder().encode(aps)
5353
let string = String(bytes: temp, encoding: .utf8)
5454
let notification = AcmeNotification(acme2: ["bang", "whiz"], aps: aps)
55-
let token = APNSwiftBearerToken(configuration: apnsConfig, timeout: 50.0)
5655

5756
do {
5857
let expiry = Date().addingTimeInterval(5)
59-
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
58+
for _ in 1...5 {
59+
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
60+
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
61+
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
62+
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
63+
}
6064
} catch {
6165
print(error)
6266
}

Sources/APNSwiftPemExample/main.swift

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,15 @@ let aps = APNSwiftPayload(alert: alert, badge: 0, sound: .critical(apsSound), ha
5757
let temp = try! JSONEncoder().encode(aps)
5858
let string = String(bytes: temp, encoding: .utf8)
5959
let notification = AcmeNotification(acme2: ["bang", "whiz"], aps: aps)
60-
let token = APNSwiftBearerToken(configuration: apnsConfig, timeout: 50.0)
6160

6261
do {
6362
let expiry = Date().addingTimeInterval(5)
64-
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
65-
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
66-
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
67-
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
68-
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
69-
try apns.send(notification, bearerToken: token, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
63+
for _ in 1...5 {
64+
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
65+
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
66+
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
67+
try apns.send(notification, pushType: .alert, to: "98AAD4A2398DDC58595F02FA307DF9A15C18B6111D1B806949549085A8E6A55D", expiration: expiry, priority: 10).wait()
68+
}
7069
} catch {
7170
print(error)
7271
}

Tests/APNSwiftJWTTests/JWTTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ final class JWTTests: XCTestCase {
3131
let teamID = "8RX5AF8F6Z"
3232
let keyID = "9N8238KQ6Z"
3333
let date = Date()
34-
let jwt = APNSwiftJWT(keyID: keyID, teamID: teamID, issueDate: date, expireDuration: 10.0)
34+
let jwt = APNSwiftJWT(keyID: keyID, teamID: teamID, issueDate: date)
3535
let token = try jwt.getDigest()
3636

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

6464
let privateKey = """
6565
-----BEGIN EC PRIVATE KEY-----

Tests/APNSwiftTests/APNSwiftConfigurationTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class APNSwiftConfigurationTests: XCTestCase {
5252
let teamID = "8RX5AF8F6Z"
5353
let keyID = "9N8238KQ6Z"
5454
let date = Date()
55-
let jwt = APNSwiftJWT(keyID: keyID, teamID: teamID, issueDate: date, expireDuration: 10.0)
55+
let jwt = APNSwiftJWT(keyID: keyID, teamID: teamID, issueDate: date)
5656
let digestValues = try jwt.getDigest()
5757
let _ = try signer.sign(digest: digestValues.fixedDigest)
5858

0 commit comments

Comments
 (0)