Skip to content

Commit e911834

Browse files
committed
Updated sample app to support auth_time
1 parent 0762758 commit e911834

5 files changed

Lines changed: 49 additions & 3 deletions

File tree

Samples/Swift/DaysUntilBirthday/Shared/DaysUntilBirthday.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct DaysUntilBirthday: App {
2929
GIDSignIn.sharedInstance.restorePreviousSignIn { user, error in
3030
if let user = user {
3131
self.authViewModel.state = .signedIn(user)
32+
self.authViewModel.authTime = UserDefaults.standard.object(forKey: "authTime") as? Date
3233
} else if let error = error {
3334
self.authViewModel.state = .signedOut
3435
print("There was an error restoring the previous sign-in: \(error)")

Samples/Swift/DaysUntilBirthday/Shared/Services/GoogleSignInAuthenticator.swift

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@ final class GoogleSignInAuthenticator: ObservableObject {
3636
return
3737
}
3838
let manualNonce = UUID().uuidString
39+
let tokenClaims: Set<GIDTokenClaim> = Set([GIDTokenClaim.authTime()])
3940

4041
GIDSignIn.sharedInstance.signIn(
4142
withPresenting: rootViewController,
4243
hint: nil,
4344
additionalScopes: nil,
44-
nonce: manualNonce
45+
nonce: manualNonce,
46+
tokenClaims: tokenClaims
4547
) { signInResult, error in
4648
guard let signInResult = signInResult else {
4749
print("Error! \(String(describing: error))")
@@ -57,6 +59,10 @@ final class GoogleSignInAuthenticator: ObservableObject {
5759
assertionFailure("ERROR: Returned nonce doesn't match manual nonce!")
5860
return
5961
}
62+
if let authTimeDate = self.decodeAuthTime(fromJWT: idToken) {
63+
self.authViewModel.authTime = authTimeDate
64+
UserDefaults.standard.set(authTimeDate, forKey: "authTime")
65+
}
6066
self.authViewModel.state = .signedIn(signInResult.user)
6167
}
6268

@@ -66,11 +72,27 @@ final class GoogleSignInAuthenticator: ObservableObject {
6672
return
6773
}
6874

69-
GIDSignIn.sharedInstance.signIn(withPresenting: presentingWindow) { signInResult, error in
75+
let tokenClaims: Set<GIDTokenClaim> = Set([GIDTokenClaim.authTime()])
76+
77+
GIDSignIn.sharedInstance.signIn(
78+
withPresenting: presentingWindow,
79+
tokenClaims: tokenClaims
80+
) { signInResult, error in
7081
guard let signInResult = signInResult else {
7182
print("Error! \(String(describing: error))")
7283
return
7384
}
85+
86+
// If the idToken is nil, we cannot get the authTime, so we treat this
87+
// as a failure for the app's sign-in flow and return.
88+
guard let idToken = signInResult.user.idToken?.tokenString else {
89+
print("Error: idToken is missing from signInResult.")
90+
return
91+
}
92+
if let authTimeDate = self.decodeAuthTime(fromJWT: idToken) {
93+
self.authViewModel.authTime = authTimeDate
94+
UserDefaults.standard.set(authTimeDate, forKey: "authTime")
95+
}
7496
self.authViewModel.state = .signedIn(signInResult.user)
7597
}
7698
#endif
@@ -154,6 +176,16 @@ private extension GoogleSignInAuthenticator {
154176
return nonce
155177
}
156178

179+
func decodeAuthTime(fromJWT jwt: String) -> Date? {
180+
let segments = jwt.components(separatedBy: ".")
181+
guard segments.count > 1,
182+
let parts = decodeJWTSegment(segments[1]),
183+
let authTimeInterval = parts["auth_time"] as? TimeInterval else {
184+
return nil
185+
}
186+
return Date(timeIntervalSince1970: authTimeInterval)
187+
}
188+
157189
func decodeJWTSegment(_ segment: String) -> [String: Any]? {
158190
guard let segmentData = base64UrlDecode(segment),
159191
let segmentJSON = try? JSONSerialization.jsonObject(with: segmentData, options: []),

Samples/Swift/DaysUntilBirthday/Shared/ViewModels/AuthenticationViewModel.swift

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ 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?
2526
private var authenticator: GoogleSignInAuthenticator {
2627
return GoogleSignInAuthenticator(authViewModel: self)
2728
}
@@ -69,7 +70,13 @@ final class AuthenticationViewModel: ObservableObject {
6970
@MainActor func addBirthdayReadScope(completion: @escaping () -> Void) {
7071
authenticator.addBirthdayReadScope(completion: completion)
7172
}
72-
73+
74+
var formattedAuthTimeString: String? {
75+
guard let date = authTime else { return nil }
76+
let formatter = DateFormatter()
77+
formatter.dateFormat = "dd/MM/yyyy"
78+
return formatter.string(from: date)
79+
}
7380
}
7481

7582
extension AuthenticationViewModel {

Samples/Swift/DaysUntilBirthday/iOS/UserProfileView.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ struct UserProfileView: View {
3535
Text(userProfile.name)
3636
.font(.headline)
3737
Text(userProfile.email)
38+
if let authTimeString = authViewModel.formattedAuthTimeString {
39+
Text("Last sign-in date: \(authTimeString)")
40+
}
3841
}
3942
}
4043
NavigationLink(NSLocalizedString("View Days Until Birthday", comment: "View birthday days"),

Samples/Swift/DaysUntilBirthday/macOS/UserProfileView.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ struct UserProfileView: View {
1919
Text(userProfile.name)
2020
.font(.headline)
2121
Text(userProfile.email)
22+
if let authTimeString = authViewModel.formattedAuthTimeString {
23+
Text("Last sign-in date: \(authTimeString)")
24+
}
2225
}
2326
}
2427
Button(NSLocalizedString("Sign Out", comment: "Sign out button"), action: signOut)

0 commit comments

Comments
 (0)