Răsfoiți Sursa

Initial update

Akshat Gandhi 6 luni în urmă
părinte
comite
ec6f69a22d

+ 41 - 0
Samples/Swift/DaysUntilBirthday/Shared/DaysUntilBirthday.swift

@@ -28,6 +28,9 @@ struct DaysUntilBirthday: App {
         .onAppear {
           GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
             if let user = user {
+              print("✅ Session restored for user: \(user.profile?.name ?? "Unknown")")
+              print("Token %@", user.idToken?.tokenString ?? "NULL")
+              self.authViewModel.authTime = user.authTime
               self.authViewModel.state = .signedIn(user)
             } else if let error = error {
               self.authViewModel.state = .signedOut
@@ -43,3 +46,41 @@ struct DaysUntilBirthday: App {
     }
   }
 }
+
+import Foundation // Make sure to add this import for the new code
+
+// This extension adds the .authTime property to the GIDGoogleUser object
+extension GIDGoogleUser {
+  var authTime: Date? {
+    guard let idToken = self.idToken?.tokenString,
+          let payload = JWTDecoder.decode(jwtToken: idToken),
+          let authTimeInterval = payload["auth_time"] as? TimeInterval else {
+      return nil
+    }
+    return Date(timeIntervalSince1970: authTimeInterval)
+  }
+}
+
+// This is a helper utility that the extension uses to decode the token
+struct JWTDecoder {
+    static func decode(jwtToken jwt: String) -> [String: Any]? {
+        let segments = jwt.components(separatedBy: ".")
+        guard segments.count > 1 else { return nil }
+        var base64 = segments[1]
+            .replacingOccurrences(of: "-", with: "+")
+            .replacingOccurrences(of: "_", with: "/")
+        let length = Double(base64.lengthOfBytes(using: .utf8))
+        let requiredLength = 4 * ceil(length / 4.0)
+        let paddingLength = requiredLength - length
+        if paddingLength > 0 {
+            let padding = "".padding(toLength: Int(paddingLength), withPad: "=", startingAt: 0)
+            base64 += padding
+        }
+        guard let bodyData = Data(base64Encoded: base64, options: .ignoreUnknownCharacters),
+              let json = try? JSONSerialization.jsonObject(with: bodyData, options: []),
+              let payload = json as? [String: Any] else {
+            return nil
+        }
+        return payload
+    }
+}

+ 16 - 1
Samples/Swift/DaysUntilBirthday/Shared/Services/GoogleSignInAuthenticator.swift

@@ -36,12 +36,14 @@ final class GoogleSignInAuthenticator: ObservableObject {
       return
     }
     let manualNonce = UUID().uuidString
+    let tokenClaims: Set<GIDTokenClaim> = Set([GIDTokenClaim.authTime()])
 
     GIDSignIn.sharedInstance.signIn(
       withPresenting: rootViewController,
       hint: nil,
       additionalScopes: nil,
-      nonce: manualNonce
+      nonce: manualNonce,
+      tokenClaims:tokenClaims
     ) { signInResult, error in
       guard let signInResult = signInResult else {
         print("Error! \(String(describing: error))")
@@ -57,6 +59,9 @@ final class GoogleSignInAuthenticator: ObservableObject {
         assertionFailure("ERROR: Returned nonce doesn't match manual nonce!")
         return
       }
+      if let authTimeDate = self.decodeAuthTime(fromJWT: idToken) {
+        self.authViewModel.authTime = authTimeDate
+      }
       self.authViewModel.state = .signedIn(signInResult.user)
     }
 
@@ -154,6 +159,16 @@ private extension GoogleSignInAuthenticator {
     return nonce
   }
 
+  func decodeAuthTime(fromJWT jwt: String) -> Date? {
+    let segments = jwt.components(separatedBy: ".")
+    guard segments.count > 1,
+          let parts = decodeJWTSegment(segments[1]),
+          let authTimeInterval = parts["auth_time"] as? TimeInterval else {
+      return nil
+    }
+    return Date(timeIntervalSince1970: authTimeInterval)
+  }
+
   func decodeJWTSegment(_ segment: String) -> [String: Any]? {
     guard let segmentData = base64UrlDecode(segment),
           let segmentJSON = try? JSONSerialization.jsonObject(with: segmentData, options: []),

+ 1 - 0
Samples/Swift/DaysUntilBirthday/Shared/ViewModels/AuthenticationViewModel.swift

@@ -22,6 +22,7 @@ final class AuthenticationViewModel: ObservableObject {
   /// The user's log in status.
   /// - note: This will publish updates when its value changes.
   @Published var state: State
+  @Published var authTime: Date?
   private var authenticator: GoogleSignInAuthenticator {
     return GoogleSignInAuthenticator(authViewModel: self)
   }

+ 13 - 0
Samples/Swift/DaysUntilBirthday/iOS/UserProfileView.swift

@@ -24,6 +24,13 @@ struct UserProfileView: View {
     return GIDSignIn.sharedInstance.currentUser
   }
 
+  private static var authTimeFormatter: DateFormatter = {
+    let formatter = DateFormatter()
+    formatter.dateStyle = .medium
+    formatter.timeStyle = .short
+    return formatter
+  }()
+
   var body: some View {
     return Group {
       if let userProfile = user?.profile {
@@ -35,6 +42,12 @@ struct UserProfileView: View {
               Text(userProfile.name)
                 .font(.headline)
               Text(userProfile.email)
+              if let authTime = authViewModel.authTime {
+                Text("Last Login: \(Self.authTimeFormatter.string(from: authTime))")
+
+              } else {
+                Text("Last Login: Not available")
+              }
             }
           }
           NavigationLink(NSLocalizedString("View Days Until Birthday", comment: "View birthday days"),

+ 13 - 0
Samples/Swift/DaysUntilBirthday/macOS/UserProfileView.swift

@@ -8,6 +8,13 @@ struct UserProfileView: View {
     return GIDSignIn.sharedInstance.currentUser
   }
 
+  private static var authTimeFormatter: DateFormatter = {
+    let formatter = DateFormatter()
+    formatter.dateStyle = .medium
+    formatter.timeStyle = .short
+    return formatter
+  }()
+
   var body: some View {
     return Group {
       if let userProfile = user?.profile {
@@ -19,6 +26,12 @@ struct UserProfileView: View {
               Text(userProfile.name)
                 .font(.headline)
               Text(userProfile.email)
+              if let authTime = authViewModel.authTime {
+                Text("Last Login: \(Self.authTimeFormatter.string(from: authTime))")
+
+              } else {
+                Text("Last Login: Not available")
+              }
             }
           }
           Button(NSLocalizedString("Sign Out", comment: "Sign out button"), action: signOut)