@@ -17,66 +17,72 @@ func base64UrlEncoded(_ data: Data) -> String {
1717 return b64
1818}
1919
20- func createJwt( ) -> String ? {
21- var error : Unmanaged < CFError > ?
2220
23- do {
24- let privateKey = try retrieveKeyFromKeyChain ( )
25-
26- // Get the public key.
27- let publicKey = privateKey. publicKey
28-
29- // Get the raw representation of the public key.
30- let rawPublicKey = publicKey. rawRepresentation
31-
32- // Extract the x and y coordinates.
33- let xCoordinateData = rawPublicKey [ 1 ..< 33 ]
34- let yCoordinateData = rawPublicKey [ 33 ..< 65 ]
35-
36- // If you need base64-encoded strings for JWK:
37- let xCoordinateBase64 = base64UrlEncoded ( xCoordinateData)
38- let yCoordinateBase64 = base64UrlEncoded ( yCoordinateData)
39- // Convert the public key to JWK format.
40- // construct headers
41- var headers : [ String : Any ] = [ " typ " : " dpop+jwt " , " alg " : " ES256 " ]
42- headers [ " jwk " ] = [
43- " kty " : " EC " ,
44- " crv " : " P-256 " ,
45- " x " : xCoordinateBase64,
46- " y " : yCoordinateBase64
47- ] as [ String : Any ]
48-
49- let headersData = try JSONSerialization . data ( withJSONObject: headers)
50- let headersB64 = base64UrlEncoded ( headersData)
51-
52-
53- // construct claims
54- let iat = Int ( Date ( ) . timeIntervalSince1970)
55- let jti = UUID ( ) . uuidString. lowercased ( )
56-
57- let claims : [ String : Any ] = [ " iat " : iat, " jti " : jti]
58- let claimsData = try JSONSerialization . data ( withJSONObject: claims)
59- let claimsB64 = base64UrlEncoded ( claimsData)
60-
61- /// sign
62- let signingInput = headersB64 + " . " + claimsB64
63- let signingInputData = signingInput. data ( using: . utf8) !
64-
65- guard let signature = try ? privateKey. signature ( for: signingInputData) else {
66- // This can happen when the [secure enclave biometrics](https://developer.apple.com/forums/thread/682162?answerId=726099022#726099022) have changed such as when an app is auto-installed on a new device
67- return nil
21+ func createJwt( ) -> String ? {
22+ var attempts = 0
23+
24+ while attempts < 3 { // Attempt the retry flow at a maximum of 3 times before giving up
25+ do {
26+ let privateKey = try retrieveKeyFromKeyChain ( )
27+
28+ // Get the public key.
29+ let publicKey = privateKey. publicKey
30+
31+ // Get the raw representation of the public key.
32+ let rawPublicKey = publicKey. rawRepresentation
33+
34+ // Extract the x and y coordinates.
35+ let xCoordinateData = rawPublicKey [ 1 ..< 33 ]
36+ let yCoordinateData = rawPublicKey [ 33 ..< 65 ]
37+
38+ // If you need base64-encoded strings for JWK:
39+ let xCoordinateBase64 = base64UrlEncoded ( xCoordinateData)
40+ let yCoordinateBase64 = base64UrlEncoded ( yCoordinateData)
41+
42+ // Convert the public key to JWK format.
43+ // construct headers
44+ var headers : [ String : Any ] = [ " typ " : " dpop+jwt " , " alg " : " ES256 " ]
45+ headers [ " jwk " ] = [
46+ " kty " : " EC " ,
47+ " crv " : " P-256 " ,
48+ " x " : xCoordinateBase64,
49+ " y " : yCoordinateBase64
50+ ] as [ String : Any ]
51+
52+ let headersData = try JSONSerialization . data ( withJSONObject: headers)
53+ let headersB64 = base64UrlEncoded ( headersData)
54+
55+ // construct claims
56+ let iat = Int ( Date ( ) . timeIntervalSince1970)
57+ let jti = UUID ( ) . uuidString. lowercased ( )
58+
59+ let claims : [ String : Any ] = [ " iat " : iat, " jti " : jti]
60+ let claimsData = try JSONSerialization . data ( withJSONObject: claims)
61+ let claimsB64 = base64UrlEncoded ( claimsData)
62+
63+ /// sign
64+ let signingInput = headersB64 + " . " + claimsB64
65+
66+ guard let signingInputData = signingInput. data ( using: . utf8) ,
67+ let signature = try ? privateKey. signature ( for: signingInputData) else {
68+ // This can happen when the [secure enclave biometrics](https://developer.apple.com/forums/thread/682162?answerId=726099022#726099022)
69+ // have changed such as when an app is auto-installed on a new device, ergo delete previously created key and try again
70+ try deleteKeyFromKeyChain ( )
71+ attempts += 1
72+ continue
73+ }
74+
75+ let signatureB64 = base64UrlEncoded ( signature. rawRepresentation)
76+ let jwt = signingInput + " . " + signatureB64
77+
78+ return jwt
79+
80+ } catch {
81+ // Handle error (log, throw, or break as necessary)
82+ print ( " An error occurred: \( error) " )
83+ break
6884 }
69-
70- let signatureB64 = base64UrlEncoded ( signature. rawRepresentation)
71-
72- let jwt = signingInput + " . " + signatureB64
73-
74- return jwt
75-
76- } catch {
77- // silently handled error
78- return nil
7985 }
80-
86+ return nil // Return nil if unable to generate JWT after 3x retries
8187}
8288
0 commit comments