Skip to content

Commit 211aa85

Browse files
authored
Merge pull request #76 from auth0/add-string-code-formata-otp
Allow to send custom NumberFormatter for otp
2 parents bafaebc + 0a4e4d6 commit 211aa85

File tree

3 files changed

+39
-16
lines changed

3 files changed

+39
-16
lines changed

Guardian/Enrollment/EnrolledDevice.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,11 @@ public struct OTPParameters: Codable {
139139
case period
140140
}
141141

142-
public init(base32Secret: String, algorithm: HMACAlgorithm = .sha1, digits: Int = 6, period: Int = 30) {
142+
public init(base32Secret: String, algorithm: HMACAlgorithm? = nil, digits: Int? = nil, period: Int? = nil) {
143143
self.base32Secret = base32Secret
144-
self.algorithm = algorithm
145-
self.digits = digits
146-
self.period = period
144+
self.algorithm = algorithm ?? .sha1
145+
self.digits = digits ?? 6
146+
self.period = period ?? 30
147147
}
148148

149149
}

Guardian/Generators/OneTimePasswordGenerator.swift

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@
2323
import Foundation
2424

2525
public protocol TOTP {
26-
func stringCode(time: TimeInterval) -> String
26+
func stringCode(time: TimeInterval, formatter: NumberFormatter?) -> String
2727
func code(time: TimeInterval) -> Int
2828
}
2929

30-
extension TOTP {
31-
public func stringCode() -> String {
32-
return self.stringCode(time: Date().timeIntervalSince1970)
30+
public extension TOTP {
31+
public func stringCode(time: TimeInterval = Date().timeIntervalSince1970, formatter: NumberFormatter? = nil) -> String {
32+
return self.stringCode(time: time, formatter: formatter)
3333
}
3434

3535
public func code() -> Int {
@@ -38,7 +38,7 @@ extension TOTP {
3838
}
3939

4040
public protocol HOTP {
41-
func stringCode(counter: Int) -> String
41+
func stringCode(counter: Int, formatter: NumberFormatter?) -> String
4242
func code(counter: Int) -> Int
4343
}
4444

@@ -96,27 +96,29 @@ struct OneTimePasswordGenerator: TOTP, HOTP {
9696
return Int(hash)
9797
}
9898

99-
func stringCode(counter: Int) -> String {
99+
func stringCode(counter: Int, formatter: NumberFormatter? = nil) -> String {
100100
let code = self.code(counter: counter)
101-
return format(code: code, digits: self.parameters.digits)
101+
return format(code: code, digits: self.parameters.digits, formatter: formatter)
102102
}
103103

104104
func code(time: TimeInterval) -> Int {
105105
let steps = timeSteps(from: time, period: self.parameters.period)
106106
return self.code(counter: steps)
107107
}
108108

109-
func stringCode(time: TimeInterval) -> String {
109+
func stringCode(time: TimeInterval, formatter: NumberFormatter? = nil) -> String {
110110
let steps = timeSteps(from: time, period: self.parameters.period)
111111
let code = self.code(counter: steps)
112-
return format(code: code, digits: self.parameters.digits)
112+
return format(code: code, digits: self.parameters.digits, formatter: formatter)
113113
}
114114

115115
private func timeSteps(from time: TimeInterval, period: Int) -> Int {
116116
return Int(time / Double(self.parameters.period))
117117
}
118118

119-
private func format(code: Int, digits: Int) -> String {
120-
return String(format: "%0\(digits)d", code)
119+
private func format(code: Int, digits: Int, formatter: NumberFormatter?) -> String {
120+
let defaultFormatted = String(format: "%0\(digits)d", code)
121+
guard let formatter = formatter else { return defaultFormatted }
122+
return formatter.string(from: NSNumber(value: code)) ?? defaultFormatted
121123
}
122124
}

GuardianTests/Generators/OneTimePasswordGeneratorSpec.swift

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,16 @@ class OneTimePasswordGeneratorSpec: QuickSpec {
6969
expect(otp.stringCode(time: 49)).to(equal(otp.code(time: 49).description)) // Picked time that generates a number with 6 digits and no 0 prefix in string
7070
}
7171

72+
it("should allow custom format for string code") {
73+
let formatter = NumberFormatter()
74+
formatter.usesGroupingSeparator = true
75+
formatter.groupingSeparator = " "
76+
formatter.groupingSize = 3
77+
formatter.minimumIntegerDigits = 6
78+
formatter.paddingCharacter = "0"
79+
expect(otp.stringCode(time: 49, formatter: formatter)).to(equal("287 082"))
80+
}
81+
7282
context("sha1") {
7383
let key = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"
7484
let alg = HMACAlgorithm.sha1
@@ -121,10 +131,21 @@ class OneTimePasswordGeneratorSpec: QuickSpec {
121131
}
122132

123133
it("should return code '\(code)' for counter '\(counter)'") {
124-
expect(otp.stringCode(counter: counter)).to(equal(code))
134+
expect(otp.stringCode(counter: counter, formatter: nil)).to(equal(code))
125135
}
126136
}
127137

138+
it("should allow custom format for string code") {
139+
let formatter = NumberFormatter()
140+
formatter.usesGroupingSeparator = true
141+
formatter.groupingSeparator = " "
142+
formatter.groupingSize = 3
143+
formatter.minimumIntegerDigits = 6
144+
formatter.paddingCharacter = "0"
145+
let otp = try! Guardian.hotp(base32Secret: "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", algorithm: .sha1, digits: 6)
146+
expect(otp.stringCode(counter: 0, formatter: formatter)).to(equal("755 224"))
147+
}
148+
128149
context("sha1") {
129150
let key = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ" // Base32.encode("12345678901234567890")
130151
let alg = HMACAlgorithm.sha1

0 commit comments

Comments
 (0)