如何在通过身份验证后注销 Google
How to Sign Out of Google After Being Authenticated
所以我的应用可以选择使用 Google 登录。单击 Google 提供的按钮后,将打开一个 Web 视图并让用户输入他们的凭据。在允许应用程序访问他们的信息后,应用程序然后让用户登录并将 SignInViewController 更改为 TabBarController(他们现在可以在其中进行相应的交互)。
当用户按下“注销”按钮时,他们会像预期的那样被定向到登录屏幕。但奇怪的是,如果用户再次按下 google 按钮,他们会自动登录,根本不需要进一步的身份验证,也没有删除他们帐户的选项。他们是清除 google 帐户凭据以保护用户免遭意外盗窃的方法吗?
签到功能:
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!, withError error: NSError!) {
if let error = error {
print(error.localizedDescription)
return
}
let authentication = user.authentication
let credential = FIRGoogleAuthProvider.credentialWithIDToken(authentication.idToken, accessToken: authentication.accessToken)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
// ...
SignInViewController().signedIn(user)
}
// ...
}
退出功能:
func signOutOverride() {
do {
try! FIRAuth.auth()!.signOut()
CredentialState.sharedInstance.signedIn = false
// Set the view to the login screen after signing out
let storyboard = UIStoryboard(name: "SignIn", bundle: nil)
let loginVC = storyboard.instantiateViewControllerWithIdentifier("SignInVC") as! SignInViewController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = loginVC
} catch let signOutError as NSError {
print ("Error signing out: \(signOutError)")
}
}
是的,就像@Rahul 所说的那样,下面的代码是正确的方法。
GIDSignIn.sharedInstance().signOut()
https://developers.google.com/identity/sign-in/ios/sign-in?ver=swift#sign_out_the_user
Swift
尝试GIDSignIn.sharedInstance().signOut()
objective - c
[[GIDSignIn sharedInstance] signOut];
想在玩过 GoogleSignIn
SDK 后详细说明之前的答案。
我看到了 signOut()
和 disconnect()
方法,想知道它们有什么区别。
signOut()
是同步调用:
// Immediately sets GIDSignIn.sharedInstance()?.currentUser to nil.
// For example, if the user is already signed in:
print(GIDSignIn.sharedInstance()?.currentUser != nil) // true - signed in
GIDSignIn.sharedInstance()?.signOut()
print(GIDSignIn.sharedInstance()?.currentUser != nil) // false - signed out
disconnect()
允许用户撤销对应用程序的访问 除了 注销。我认为这意味着如果他们选择再次登录,他们将需要重新授予对您的应用程序的任何权限。
根据 Google's Developer Docs,如果用户选择 断开 与您的应用的连接,那么您需要删除用户的任何 Google 数据已存储在您的应用中。
此外,disconnect()
是异步的。断开连接调用的结果将返回给 GIDSignInDelegate.sign(_:didDisconnectWith:withError:)
方法。
// Also sets GIDSignIn.sharedInstance()?.currentUser to nil.
// Asynchronous call. If for example the user was already signed in:
print(GIDSignIn.sharedInstance()?.currentUser != nil) // true - signed in
GIDSignIn.sharedInstance()?.disconnect()
print(GIDSignIn.sharedInstance()?.currentUser != nil) // true - still signed in
// MARK: - GIDSignInDelegate
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
print(GIDSignIn.sharedInstance()?.currentUser != nil) // false - signed out
// Remove any data saved to your app obtained from Google's APIs for this user.
}
public func logOut(on:UIViewController){
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
GIDSignIn.sharedInstance().signOut()
GIDSignIn.sharedInstance().disconnect()
if let url = NSURL(string: "https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue=https://google.com"){
UIApplication.shared.open(url as URL, options: [:]) { (true) in
let appDel:AppDelegate = UIApplication.shared.delegate as! AppDelegate
appDel.window?.rootViewController = LoginViewController()
}
}
} catch let signOutError as NSError {
Help.shared.Snack(messageString: "Error signing out: \(signOutError)"
)
print ("Error signing out: %@", signOutError)
}
}
为什么用户在注销后不需要重新输入密码?
我想澄清一下为什么用户立即重新登录以及为什么其中一些解决方案在 iOS 13 上不起作用。
实际上,以下确实将用户从您的应用中注销:
GIDSignIn.sharedInstance().signOut()
但它不会在 Safari 本身中将用户从 google 中注销!!亲自尝试一下,在您的应用中登录 google 帐户。从应用程序注销,然后在您的 Safari 浏览器中转到 accounts.google.com,该帐户仍将处于登录状态!所以 cookie 正在与默认的 Safari 浏览器共享,但为什么呢?
经过一些调查后,我在 OIDExternalUserAgentiOS class 中找到了这个,它处理实际的交互式身份验证流程。
// iOS 12 and later, use ASWebAuthenticationSession
if (@available(iOS 12.0, *)) {
// ASWebAuthenticationSession doesn't work with guided access (rdar://40809553)
if (!UIAccessibilityIsGuidedAccessEnabled()) {
__weak OIDExternalUserAgentIOS *weakSelf = self;
NSString *redirectScheme = request.redirectScheme;
ASWebAuthenticationSession *authenticationVC =
[[ASWebAuthenticationSession alloc] initWithURL:requestURL
callbackURLScheme:redirectScheme
completionHandler:^(NSURL * _Nullable callbackURL,
NSError * _Nullable error) {
__strong OIDExternalUserAgentIOS *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
strongSelf->_webAuthenticationVC = nil;
if (callbackURL) {
[strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL];
} else {
NSError *safariError =
[OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow
underlyingError:error
description:nil];
[strongSelf->_session failExternalUserAgentFlowWithError:safariError];
}
}];
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13.0, *)) {
authenticationVC.presentationContextProvider = self;
}
#endif
_webAuthenticationVC = authenticationVC;
openedUserAgent = [authenticationVC start];
}
}
// iOS 11, use SFAuthenticationSession
if (@available(iOS 11.0, *)) {
// SFAuthenticationSession doesn't work with guided access (rdar://40809553)
if (!openedUserAgent && !UIAccessibilityIsGuidedAccessEnabled()) {
__weak OIDExternalUserAgentIOS *weakSelf = self;
NSString *redirectScheme = request.redirectScheme;
SFAuthenticationSession *authenticationVC =
[[SFAuthenticationSession alloc] initWithURL:requestURL
callbackURLScheme:redirectScheme
completionHandler:^(NSURL * _Nullable callbackURL,
NSError * _Nullable error) {
__strong OIDExternalUserAgentIOS *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
strongSelf->_authenticationVC = nil;
if (callbackURL) {
[strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL];
} else {
NSError *safariError =
[OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow
underlyingError:error
description:@"User cancelled."];
[strongSelf->_session failExternalUserAgentFlowWithError:safariError];
}
}];
_authenticationVC = authenticationVC;
openedUserAgent = [authenticationVC start];
}
}
// iOS 9 and 10, use SFSafariViewController
if (@available(iOS 9.0, *)) {
if (!openedUserAgent && _presentingViewController) {
SFSafariViewController *safariVC =
[[SFSafariViewController alloc] initWithURL:requestURL];
safariVC.delegate = self;
_safariVC = safariVC;
[_presentingViewController presentViewController:safariVC animated:YES completion:nil];
openedUserAgent = YES;
}
}
// iOS 8 and earlier, use mobile Safari
if (!openedUserAgent){
openedUserAgent = [[UIApplication sharedApplication] openURL:requestURL];
}
如果您使用的是 iOS 12+,它会使用 ASWebAuthenticationService
,它默认与 Safari 共享 cookie! iOS 11, 10, 9, 8 情况类似,执行方式不同。我在 Apple 文档 here 中发现了一些关于 SFSafariViewController
的有趣内容:
In iOS 9 and 10, it [SFSafariViewController]
shares cookies and other website data with Safari.
当 google 登录页面出现时,它与 Safari 共享 cookie。这就是我们没有完全退出 google 的原因。事实上,这似乎完全是故意的。
我们如何注销用户?
这适用于 GIDSignIn 支持的所有 iOS 版本:
let url = URL(string: "https://accounts.google.com/Logout")!
UIApplication.shared.open(url, options: [:], completion: nil)
不幸的是,这会将用户重定向到应用程序之外。但是您可以在调用 open
.
之前在 UIAlertController 中解释必要性
如果有人还在看这个,我想我已经按照 OP 最初要求的方式工作了。
所以在我的例子中我做了这样的事情:
GIDSignIn *gidObject = [GIDSignIn sharedInstance];
[gidObject signOut];
[gidObject disconnect];
NSString *logOutUrl = @"https://www.google.com/accounts/Logout";
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: logOutUrl] options:@{} completionHandler:nil];
我让应用程序转到 google 注销 url,如上所示。对于我们的工作流程,我希望用户主动知道他们正在注销。有点像给用户的支票。
我做错的是 GIDSignIn 的注销和断开连接。在我断开连接后继续注销之前。当我那样做时,无论用户做什么,他们都永远不会 "signed out" of google。当我撤销断开连接和注销时,它会将用户从他们的 google 帐户中注销,这正是我们想要的。
我想从逻辑上讲,人们会在断开与应用程序的连接之前先注销。
所以我的应用可以选择使用 Google 登录。单击 Google 提供的按钮后,将打开一个 Web 视图并让用户输入他们的凭据。在允许应用程序访问他们的信息后,应用程序然后让用户登录并将 SignInViewController 更改为 TabBarController(他们现在可以在其中进行相应的交互)。
当用户按下“注销”按钮时,他们会像预期的那样被定向到登录屏幕。但奇怪的是,如果用户再次按下 google 按钮,他们会自动登录,根本不需要进一步的身份验证,也没有删除他们帐户的选项。他们是清除 google 帐户凭据以保护用户免遭意外盗窃的方法吗?
签到功能:
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!, withError error: NSError!) {
if let error = error {
print(error.localizedDescription)
return
}
let authentication = user.authentication
let credential = FIRGoogleAuthProvider.credentialWithIDToken(authentication.idToken, accessToken: authentication.accessToken)
FIRAuth.auth()?.signInWithCredential(credential) { (user, error) in
// ...
SignInViewController().signedIn(user)
}
// ...
}
退出功能:
func signOutOverride() {
do {
try! FIRAuth.auth()!.signOut()
CredentialState.sharedInstance.signedIn = false
// Set the view to the login screen after signing out
let storyboard = UIStoryboard(name: "SignIn", bundle: nil)
let loginVC = storyboard.instantiateViewControllerWithIdentifier("SignInVC") as! SignInViewController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = loginVC
} catch let signOutError as NSError {
print ("Error signing out: \(signOutError)")
}
}
是的,就像@Rahul 所说的那样,下面的代码是正确的方法。
GIDSignIn.sharedInstance().signOut()
https://developers.google.com/identity/sign-in/ios/sign-in?ver=swift#sign_out_the_user
Swift
尝试GIDSignIn.sharedInstance().signOut()
objective - c
[[GIDSignIn sharedInstance] signOut];
想在玩过 GoogleSignIn
SDK 后详细说明之前的答案。
我看到了 signOut()
和 disconnect()
方法,想知道它们有什么区别。
signOut()
是同步调用:
// Immediately sets GIDSignIn.sharedInstance()?.currentUser to nil.
// For example, if the user is already signed in:
print(GIDSignIn.sharedInstance()?.currentUser != nil) // true - signed in
GIDSignIn.sharedInstance()?.signOut()
print(GIDSignIn.sharedInstance()?.currentUser != nil) // false - signed out
disconnect()
允许用户撤销对应用程序的访问 除了 注销。我认为这意味着如果他们选择再次登录,他们将需要重新授予对您的应用程序的任何权限。
根据 Google's Developer Docs,如果用户选择 断开 与您的应用的连接,那么您需要删除用户的任何 Google 数据已存储在您的应用中。
此外,disconnect()
是异步的。断开连接调用的结果将返回给 GIDSignInDelegate.sign(_:didDisconnectWith:withError:)
方法。
// Also sets GIDSignIn.sharedInstance()?.currentUser to nil.
// Asynchronous call. If for example the user was already signed in:
print(GIDSignIn.sharedInstance()?.currentUser != nil) // true - signed in
GIDSignIn.sharedInstance()?.disconnect()
print(GIDSignIn.sharedInstance()?.currentUser != nil) // true - still signed in
// MARK: - GIDSignInDelegate
func sign(_ signIn: GIDSignIn!, didDisconnectWith user: GIDGoogleUser!, withError error: Error!) {
print(GIDSignIn.sharedInstance()?.currentUser != nil) // false - signed out
// Remove any data saved to your app obtained from Google's APIs for this user.
}
public func logOut(on:UIViewController){
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
GIDSignIn.sharedInstance().signOut()
GIDSignIn.sharedInstance().disconnect()
if let url = NSURL(string: "https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue=https://google.com"){
UIApplication.shared.open(url as URL, options: [:]) { (true) in
let appDel:AppDelegate = UIApplication.shared.delegate as! AppDelegate
appDel.window?.rootViewController = LoginViewController()
}
}
} catch let signOutError as NSError {
Help.shared.Snack(messageString: "Error signing out: \(signOutError)"
)
print ("Error signing out: %@", signOutError)
}
}
为什么用户在注销后不需要重新输入密码?
我想澄清一下为什么用户立即重新登录以及为什么其中一些解决方案在 iOS 13 上不起作用。
实际上,以下确实将用户从您的应用中注销:
GIDSignIn.sharedInstance().signOut()
但它不会在 Safari 本身中将用户从 google 中注销!!亲自尝试一下,在您的应用中登录 google 帐户。从应用程序注销,然后在您的 Safari 浏览器中转到 accounts.google.com,该帐户仍将处于登录状态!所以 cookie 正在与默认的 Safari 浏览器共享,但为什么呢?
经过一些调查后,我在 OIDExternalUserAgentiOS class 中找到了这个,它处理实际的交互式身份验证流程。
// iOS 12 and later, use ASWebAuthenticationSession
if (@available(iOS 12.0, *)) {
// ASWebAuthenticationSession doesn't work with guided access (rdar://40809553)
if (!UIAccessibilityIsGuidedAccessEnabled()) {
__weak OIDExternalUserAgentIOS *weakSelf = self;
NSString *redirectScheme = request.redirectScheme;
ASWebAuthenticationSession *authenticationVC =
[[ASWebAuthenticationSession alloc] initWithURL:requestURL
callbackURLScheme:redirectScheme
completionHandler:^(NSURL * _Nullable callbackURL,
NSError * _Nullable error) {
__strong OIDExternalUserAgentIOS *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
strongSelf->_webAuthenticationVC = nil;
if (callbackURL) {
[strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL];
} else {
NSError *safariError =
[OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow
underlyingError:error
description:nil];
[strongSelf->_session failExternalUserAgentFlowWithError:safariError];
}
}];
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
if (@available(iOS 13.0, *)) {
authenticationVC.presentationContextProvider = self;
}
#endif
_webAuthenticationVC = authenticationVC;
openedUserAgent = [authenticationVC start];
}
}
// iOS 11, use SFAuthenticationSession
if (@available(iOS 11.0, *)) {
// SFAuthenticationSession doesn't work with guided access (rdar://40809553)
if (!openedUserAgent && !UIAccessibilityIsGuidedAccessEnabled()) {
__weak OIDExternalUserAgentIOS *weakSelf = self;
NSString *redirectScheme = request.redirectScheme;
SFAuthenticationSession *authenticationVC =
[[SFAuthenticationSession alloc] initWithURL:requestURL
callbackURLScheme:redirectScheme
completionHandler:^(NSURL * _Nullable callbackURL,
NSError * _Nullable error) {
__strong OIDExternalUserAgentIOS *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
strongSelf->_authenticationVC = nil;
if (callbackURL) {
[strongSelf->_session resumeExternalUserAgentFlowWithURL:callbackURL];
} else {
NSError *safariError =
[OIDErrorUtilities errorWithCode:OIDErrorCodeUserCanceledAuthorizationFlow
underlyingError:error
description:@"User cancelled."];
[strongSelf->_session failExternalUserAgentFlowWithError:safariError];
}
}];
_authenticationVC = authenticationVC;
openedUserAgent = [authenticationVC start];
}
}
// iOS 9 and 10, use SFSafariViewController
if (@available(iOS 9.0, *)) {
if (!openedUserAgent && _presentingViewController) {
SFSafariViewController *safariVC =
[[SFSafariViewController alloc] initWithURL:requestURL];
safariVC.delegate = self;
_safariVC = safariVC;
[_presentingViewController presentViewController:safariVC animated:YES completion:nil];
openedUserAgent = YES;
}
}
// iOS 8 and earlier, use mobile Safari
if (!openedUserAgent){
openedUserAgent = [[UIApplication sharedApplication] openURL:requestURL];
}
如果您使用的是 iOS 12+,它会使用 ASWebAuthenticationService
,它默认与 Safari 共享 cookie! iOS 11, 10, 9, 8 情况类似,执行方式不同。我在 Apple 文档 here 中发现了一些关于 SFSafariViewController
的有趣内容:
In iOS 9 and 10, it
[SFSafariViewController]
shares cookies and other website data with Safari.
当 google 登录页面出现时,它与 Safari 共享 cookie。这就是我们没有完全退出 google 的原因。事实上,这似乎完全是故意的。
我们如何注销用户?
这适用于 GIDSignIn 支持的所有 iOS 版本:
let url = URL(string: "https://accounts.google.com/Logout")!
UIApplication.shared.open(url, options: [:], completion: nil)
不幸的是,这会将用户重定向到应用程序之外。但是您可以在调用 open
.
如果有人还在看这个,我想我已经按照 OP 最初要求的方式工作了。
所以在我的例子中我做了这样的事情:
GIDSignIn *gidObject = [GIDSignIn sharedInstance];
[gidObject signOut];
[gidObject disconnect];
NSString *logOutUrl = @"https://www.google.com/accounts/Logout";
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: logOutUrl] options:@{} completionHandler:nil];
我让应用程序转到 google 注销 url,如上所示。对于我们的工作流程,我希望用户主动知道他们正在注销。有点像给用户的支票。 我做错的是 GIDSignIn 的注销和断开连接。在我断开连接后继续注销之前。当我那样做时,无论用户做什么,他们都永远不会 "signed out" of google。当我撤销断开连接和注销时,它会将用户从他们的 google 帐户中注销,这正是我们想要的。
我想从逻辑上讲,人们会在断开与应用程序的连接之前先注销。