@@ -22,10 +22,21 @@ final class AuthenticationViewModel: ObservableObject {
2222 /// The user's log in status.
2323 /// - note: This will publish updates when its value changes.
2424 @Published var state : State
25- @Published var authTime : Date ?
2625 private var authenticator : GoogleSignInAuthenticator {
2726 return GoogleSignInAuthenticator ( authViewModel: self )
2827 }
28+
29+ // 3. Change authTime to a computed property
30+ var authTime : Date ? {
31+ switch state {
32+ case . signedIn( let user) :
33+ guard let idToken = user. idToken? . tokenString else { return nil }
34+ return decodeAuthTime ( fromJWT: idToken)
35+ case . signedOut:
36+ return nil
37+ }
38+ }
39+
2940 /// The user-authorized scopes.
3041 /// - note: If the user is logged out, then this will default to empty.
3142 var authorizedScopes : [ String ] {
@@ -79,6 +90,42 @@ final class AuthenticationViewModel: ObservableObject {
7990 }
8091}
8192
93+ private extension AuthenticationViewModel {
94+ func decodeAuthTime( fromJWT jwt: String ) -> Date ? {
95+ let segments = jwt. components ( separatedBy: " . " )
96+ guard segments. count > 1 ,
97+ let parts = decodeJWTSegment ( segments [ 1 ] ) ,
98+ let authTimeInterval = parts [ " auth_time " ] as? TimeInterval else {
99+ return nil
100+ }
101+ return Date ( timeIntervalSince1970: authTimeInterval)
102+ }
103+
104+ func decodeJWTSegment( _ segment: String ) -> [ String : Any ] ? {
105+ guard let segmentData = base64UrlDecode ( segment) ,
106+ let segmentJSON = try ? JSONSerialization . jsonObject ( with: segmentData, options: [ ] ) ,
107+ let payload = segmentJSON as? [ String : Any ] else {
108+ return nil
109+ }
110+ return payload
111+ }
112+
113+ func base64UrlDecode( _ value: String ) -> Data ? {
114+ var base64 = value
115+ . replacingOccurrences ( of: " - " , with: " + " )
116+ . replacingOccurrences ( of: " _ " , with: " / " )
117+
118+ let length = Double ( base64. lengthOfBytes ( using: String . Encoding. utf8) )
119+ let requiredLength = 4 * ceil( length / 4.0 )
120+ let paddingLength = requiredLength - length
121+ if paddingLength > 0 {
122+ let padding = " " . padding ( toLength: Int ( paddingLength) , withPad: " = " , startingAt: 0 )
123+ base64 = base64 + padding
124+ }
125+ return Data ( base64Encoded: base64, options: . ignoreUnknownCharacters)
126+ }
127+ }
128+
82129extension AuthenticationViewModel {
83130 /// An enumeration representing logged in status.
84131 enum State {
0 commit comments