AuthTokenResult.swift 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // Copyright 2023 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. import Foundation
  15. /** @var kExpirationDateKey
  16. @brief The key used to encode the expirationDate property for NSSecureCoding.
  17. */
  18. // XXX TODO: TYPO IN ORIGINAL KEY. TO FIX OR NOT?
  19. private let kExpirationDateKey = "expiratinDate"
  20. /** @var kTokenKey
  21. @brief The key used to encode the token property for NSSecureCoding.
  22. */
  23. private let kTokenKey = "token"
  24. /** @var kAuthDateKey
  25. @brief The key used to encode the authDate property for NSSecureCoding.
  26. */
  27. private let kAuthDateKey = "authDate"
  28. /** @var kIssuedDateKey
  29. @brief The key used to encode the issuedDate property for NSSecureCoding.
  30. */
  31. private let kIssuedDateKey = "issuedDate"
  32. /** @var kSignInProviderKey
  33. @brief The key used to encode the signInProvider property for NSSecureCoding.
  34. */
  35. private let kSignInProviderKey = "signInProvider"
  36. /** @var kSignInSecondFactorKey
  37. @brief The key used to encode the signInSecondFactor property for NSSecureCoding.
  38. */
  39. private let kSignInSecondFactorKey = "signInSecondFactor"
  40. /** @var kClaimsKey
  41. @brief The key used to encode the claims property for NSSecureCoding.
  42. */
  43. private let kClaimsKey = "claims"
  44. /** @class FIRAuthTokenResult
  45. @brief A data class containing the ID token JWT string and other properties associated with the
  46. token including the decoded payload claims.
  47. */
  48. @objc(FIRAuthTokenResult) public class AuthTokenResult: NSObject {
  49. /** @property token
  50. @brief Stores the JWT string of the ID token.
  51. */
  52. @objc public var token: String
  53. /** @property expirationDate
  54. @brief Stores the ID token's expiration date.
  55. */
  56. @objc public var expirationDate: Date
  57. /** @property authDate
  58. @brief Stores the ID token's authentication date.
  59. @remarks This is the date the user was signed in and NOT the date the token was refreshed.
  60. */
  61. @objc public var authDate: Date
  62. /** @property issuedAtDate
  63. @brief Stores the date that the ID token was issued.
  64. @remarks This is the date last refreshed and NOT the last authentication date.
  65. */
  66. @objc public var issuedAtDate: Date
  67. /** @property signInProvider
  68. @brief Stores sign-in provider through which the token was obtained.
  69. @remarks This does not necessarily map to provider IDs.
  70. */
  71. @objc public var signInProvider: String
  72. /** @property signInSecondFactor
  73. @brief Stores sign-in second factor through which the token was obtained.
  74. */
  75. @objc public var signInSecondFactor: String?
  76. /** @property claims
  77. @brief Stores the entire payload of claims found on the ID token. This includes the standard
  78. reserved claims as well as custom claims set by the developer via the Admin SDK.
  79. */
  80. @objc public var claims: [String: Any]
  81. /** @fn tokenResultWithToken:
  82. @brief Parse a token string to a structured token.
  83. @param token The token string to parse.
  84. @return A structured token result.
  85. */
  86. @objc public class func tokenResult(token: String) -> AuthTokenResult? {
  87. let tokenStringArray = token.components(separatedBy: ".")
  88. // The JWT should have three parts, though we only use the second in this method.
  89. if tokenStringArray.count != 3 {
  90. return nil
  91. }
  92. // The token payload is always the second index of the array.
  93. let IDToken = tokenStringArray[1]
  94. // Convert the base64URL encoded string to a base64 encoded string.
  95. // Replace "_" with "/"
  96. // Replace "-" with "+"
  97. var tokenPayload = IDToken.replacingOccurrences(of: "_", with: "/")
  98. .replacingOccurrences(of: "-", with: "+")
  99. // Pad the token payload with "=" signs if the payload's length is not a multiple of 4.
  100. if tokenPayload.count % 4 != 0 {
  101. let length = tokenPayload.count + (4 - tokenPayload.count % 4)
  102. tokenPayload = tokenPayload.padding(toLength: length, withPad: "=", startingAt: 0)
  103. }
  104. guard let decodedTokenPayloadData = Data(
  105. base64Encoded: tokenPayload,
  106. options: [.ignoreUnknownCharacters]
  107. ) else {
  108. return nil
  109. }
  110. guard let tokenPayloadDictionary = try? JSONSerialization.jsonObject(
  111. with: decodedTokenPayloadData,
  112. options: [.mutableContainers, .allowFragments]
  113. ) as? [String: Any] else {
  114. return nil
  115. }
  116. // These are dates since 00:00:00 January 1 1970, as described by the Terminology section in
  117. // the JWT spec. https://tools.ietf.org/html/rfc7519
  118. let decoder = JSONDecoder()
  119. decoder.dateDecodingStrategy = .secondsSince1970
  120. decoder.keyDecodingStrategy = .convertFromSnakeCase
  121. guard let jwt = try? decoder.decode(JWT.self, from: decodedTokenPayloadData) else {
  122. return nil
  123. }
  124. let tokenResult = AuthTokenResult(token: token,
  125. expirationDate: jwt.exp,
  126. authDate: jwt.authTime,
  127. issuedAtDate: jwt.iat,
  128. signInProvider: jwt.firebase.signInProvider,
  129. signInSecondFactor: jwt.firebase.signInSecondFactor,
  130. claims: tokenPayloadDictionary)
  131. return tokenResult
  132. }
  133. init(token: String,
  134. expirationDate: Date,
  135. authDate: Date,
  136. issuedAtDate: Date,
  137. signInProvider: String,
  138. signInSecondFactor: String?,
  139. claims: [String: Any]) {
  140. self.token = token
  141. self.expirationDate = expirationDate
  142. self.authDate = authDate
  143. self.issuedAtDate = issuedAtDate
  144. self.signInProvider = signInProvider
  145. self.signInSecondFactor = signInSecondFactor
  146. self.claims = claims
  147. }
  148. }
  149. struct JWT: Decodable {
  150. struct FirebasePayload: Decodable {
  151. let signInProvider: String
  152. let signInSecondFactor: String?
  153. }
  154. let exp: Date
  155. let authTime: Date
  156. let iat: Date
  157. let firebase: FirebasePayload
  158. }
  159. /*
  160. @implementation FIRAuthTokenResult
  161. + (nullable FIRAuthTokenResult *)tokenResultWithToken:(NSString *)token {
  162. }
  163. #pragma mark - NSSecureCoding
  164. + (BOOL)supportsSecureCoding {
  165. return YES;
  166. }
  167. - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
  168. NSString *token = [aDecoder decodeObjectOfClass:[NSDate class] forKey:kTokenKey];
  169. return [FIRAuthTokenResult tokenResultWithToken:token];
  170. }
  171. - (void)encodeWithCoder:(NSCoder *)aCoder {
  172. [aCoder encodeObject:_token forKey:kTokenKey];
  173. }
  174. @end
  175. */