I'm developing an internal app that will leverage our corporate Google Drive accounts and will be used on shared devices (iPads shared among teachers and students at school sites).
Is there a way to force GIDSignIn to require a password with each sign-in attempt? Right now, even after calling GIDSIgnIn.sharedInstance().signOut() (or GIDSignIn.sharedInstacne().disconnect()) the user doesn't need to enter their password the next time they access the app. That means, when the device is taken by the next user, they could very easily access the other user's account.
Am I missing something? Do I need to somehow clear the cookies store in the UIWebView that the GIDSignIn process uses?
Where available, the GIDSignIn login process uses a SFSafariViewController, not a UIWebView. It leverages the cookies (as well as passwords) stored in Safari, for a quicker login.
I don't think you would be able to clear such cookies. You should be able to force a Google log out, though, by opening https://www.google.com/accounts/Logout in an SFSafariViewController, though the interaction with the rest of your app may be a bit weird. See Logout link with return URL (OAuth) for a way to provide a return URL which you may try to use to control the process (you'll need to use an URL scheme to return, though).
Note that iOS may prompt to save login information, and then provide said login information to subsequent users. You'll need to disable that in Settings -> Safari -> AutoFill
There may be other ways of achieving it via configuration of the device, but iOS is not really designed for multiple users at the moment.
Related
I want to open, from an iOS app, a web page that requires authentication in order to get to that page.
I googled a little bit and I believe I need to use WebKit and Javascript injection, but I am not sure and I have never done something like this, so every bit of information is welcomed or pointing me in the right direction.
I will give an example that I hope will make things more clear(I don't actually want to open facebook, it's just part of the example):
Is it possible to do the following scenario? And if yes, how?
Open a web page from an iOS app, for example: "https://www.facebook.com/profile" without having to go through the login page? I do have the user credentials(username and password), as the user is already logged in with those credentials in the iOS app, but the requirement is to not go through the login page, but to go straight to the profile page.
In general the answer is: no. Even if the user is already logged in and has a valid authentication token that token may only be valid from within your app and not from within the browser. And the login form may be protected by something like a captche preventing you from automatically logging someone in.
There certainly are situation where it is possible: For example if the tokens are not scoped to your app you can try passing them along. Or there is an actual API that you can call with the token that logs the user into the website on the website, etc. But those depend on the specific target website or wether you can control that target website and can add this functionality.
I'm planning to switch an app from the old OAuth flow with the SFSafariViewController to the new flow with iOS 11's SFAuthenticationSession. Logging in isn't an issue, the transfer to the new API took me a few minutes to implement. However logging out has me baffled.
How?
I can't find any mentioning of wanting to offer the option of logging out anywhere in the docs. Using the old SFSafariViewController to invalidate the cookies? Nope, they're not shared anymore with SFAuthenticationSession. As soon as I restart the authentication session the user get's logged in automatically and there's no way out. So how to enable logging out? Or am I simply overlooking something completely obvious?
Update:
I found a "way that works" in a technical sense, but it's bonkers for the user: Open a new SFAuthenticationSession on the logout page that clears the cookie. But that means when logging out the alert view asks the user again whether he'd like to log in via the service. If yes is selected ("logging in"), the cookie clearing logout page is opened, the user has to manually dismiss the view, which can be caught by the completion handler and we know we can open the login view again.. displaying the login prompt to log out? I really don't like this solution.
Any ideas? Am I still overlooking a completely obvious solution?
Update 2: As no one has any clue about this issue so far, this is probably not an easy one. I have filed a suggestion with Apple via their report tool to either clarify how to handle this or build it into the API if not available. Will post if I get an answer.
Update 3: After pondering the issue a bit more we found another possible (although also unattractive) solution if you can influence the login page of the OAuth provider: make cookies very short lived. Then the login page can be opened without automatic log in. However this kills the whole purpose of sharing login sessions between apps.. and you need to be able to influence the login page.
Update 4: Since iOS 12 SFAuthenticationSession is deprecated and got replaced by ASWebAuthenticationSession. However ASWebAuthenticationSession does not change anything in regard to logging out. It's still not possible. Same issue as before.
With ASWebAuthenticationSession, setting .prefersEphemeralWebBrowserSession to true prior to calling .start() will force the user to enter credentials in the browser session. While not the same as logging out, this will allow a new user to login with different credentials when launching the next session.
Update November 2020: We used #react-native-community/cookies to clear cookies as a workaround. See the snipped below as an example.
import CookieManager from '#react-native-community/cookies';
CookieManager.clearAll().catch(e => alert("Error deleting cookies during logout"))
Previous answer from April 2020. This may be helpful for anybody struggling with this. I've spent few hours testing different options, going through apps and looking how they do it and reading forums/discussions.
I haven't find a way to programatically clear cookies and there is no documentation on Apple on this.
Using FB as an example. Logging out from Safari and deleting FB app doesn't help. Any app which is downloaded will not ask for login to FB if you logged in once before through ASWebAuthenticationSession or SFAuthenticationSession.
If users ask how to force login (even though it's not your problem as a developer) you can point them to: Settings -> Safari -> Advanced -> Website Data -> Remove All Website Data (or just the ones for the provider).
If your use case needs switching of users (like in my case where we use Azure AD and users share 1 phone) you have 2 options. A) Open ASWebAuthenticationSession with the logout endpoint (as mentioned, this is very weird UX). B) Open Safari as a separate app (not inside yours) and do login/logout there. Unfortunately, there is no way to redirect the user to your app after logout if the OAuth provider doesn't support redirect on logout.
It sucks because this prevents developers from creating nice experiences on iOS for use cases where a business needs to share device between multiple users and OAuth is used as identity provider.
One of the “best” solutions I have come across is to open a logout page in system Safari (not an SFSafariViewController). Because ASWebAuthenticationSession shares cookies reliably with Safari, the expired/deleted cookie then also affects the app.
See this GitHub page for more details.
It depends on which cookie stores your login info;
If it is a session cookie, then it is not shared with Safari as per https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession
So, simply clear your local session, and the cookies will be cleared on the next app launch.
If not, and the cookie persists, then like Martin said above, you should open Safari (not SFSafariViewController) with your logout URL, then redirect back to your app.
Please let me know if you need more info. I have tested extensively with all 3 ways of authentication (ASWebAuthenticationSession, Safari, and SFSafariViewController).
For iOS 13.0 need to add SceneDelegate.swift for UISceneConfiguration
Also need to update appdelegate for UIScene implementation
Add UISceneSession Lifecycle
It is working fine this way SFAuthenticationSession issue resolved.
In one of our apps, we've already started using ASWebAuthenticationSession.
Our use case for this goes beyond just retrieving access and refresh tokens upon login. What I mean by this is, the same session cookie is used when opening the web app (whilst logged-in to the iOS app) in order to save the user from re-authenticating themselves again and again. Eventually, time comes when the user finally decides to log out of their account and may thereafter attempt to re-login again using a different account. Since the user's session cookie may still be alive by then, any re-login attempt only flashes the authentication screen momentarily, logging them in automatically back to their first account without giving them a chance to enter the credentials of the second account.
To really force the user to enter their credentials every time we present the authentication screen, we have to add to our Auth0 query params the prompt=login pair.
Here's what the URL would look like:
https://example.auth0.com/authorize?
client_id=abcd1234
&redirect_uri= https://example.com/callback
&scope=openid profile
&response_type=id_token
&prompt=login
You can find more info about this on this Auth0 doc: https://auth0.com/docs/authenticate/login/max-age-reauthentication
On iOS 11, Apple introduced a new way to share auth data between the web and a Mobile App with SFAuthenticationSession.
It would be a bad user experience to show the SFAuthenticationSession prompt to every new users - that might have never used my website - to have them agree and then get nothing out of it and have ask them to login.
The documentation is pretty empty on Apple side. This is the only example I found.
Is there a way to know if there is a cookie available before showing the SFAuthenticationSession prompt? Or maybe, with the Associated Domains enabled, when authenticating with my domain, the system should not show the prompt?
No. Even if there aren't cookies, the user can login entering his username/password and then clicking on "Login"/"Enter" on the website (E.g: Facebook, Instagram).
Accessing the cookies won't be enabled:
When the webpage is presented, it runs in a separate process, so the user and web service are guaranteed that the app has no way to gain access to the user’s credentials. Instead, the app gets a unique authentication token.
Official docs
Working example for Instagram-OAuth: https://github.com/dvdhpkns/SFAuthenticationSession-Instagram-Oauth
And the GitHub repo you added was posted by the author to send a bug report about errors in cookie sharing for local servers (rdar://33418129. Original tweet)
Are cookies shared between Safari and SFAuthenticationSession?
#DVDHPKNS
They’re supposed to be shared, but we have some timing bugs right now. Please do file bugs about what you’re seeing.
#rmondello (Apple employee)
P.S: They added more information to the docs since your original post date.
I'm trying to intercept link and login a user, then send them on to Safari and have the page load with no authentication request.
So, what I'm doing so far...
I register a custom URL scheme for my app. Call it "myhttp". Now someone clicks on a link (say from an email) of myhttp://secured.com/foo and my app runs. The apps pulls the user's credentials from somewhere and makes a call to the real URL with an NSURLConnection. The NSURLConectionDelegate implements connection:didReceiveAuthenticationChallenge and I navigate through the security layer fine. Next I try loading the same url using the UIApplication openURL method to bring up Safari, but I still get an authentication check.
I thought this would work because I read the follow in the Apple documentation.
Credentials stored in persistent storage are kept in the user’s keychain and shared among all apps.
And when I check the NSURLCredentialStorage I can see the credentials I just used stored there with the correct information, protection space, scheme, etc, but clearly I'm doing something wrong or I wouldn't be getting an authentication challenge when I switch to Safari.
So the question is, did I just screw up somewhere along the line, forget some important bit or am I going about this the wrong way?
This wasn't possible before iOS 8, but is now with the Shared Web Credentials feature.
Add a com.apple.developer.associated-domains entitlement to your app.
This entitlement must include all the domains with which you want to
share credentials.
Add an apple-app-site-association file to your website. This file must
include application identifiers for all the apps with which the site
wants to share credentials, and it must be properly signed.
When the app is installed, the system downloads and verifies the site
association file for each of its associated domains. If the
verification is successful, the app is associated with the domain.
An app can share credentials with any associated domains by calling
SecAddSharedWebCredential and SecRequestSharedWebCredential.
I have a need to set some context via Safari (a context token), and then read that context from a native iOS app. What are the best practices for doing this?
A couple thoughts so far:
Set the context in an HTML 5 database, but I'm not sure this will work because the database might be only accessible from Safari. Would using a WebUIView in the native app allow me to access the same HTML5 database / local storage as Safari?
Set the context in device storage, but I'm not sure this will work because I don't know if Safari can actually write to device storage.
I would suggest one of these two options:
Let the web server keep track on the user both in the app and on the website, for example by creating a user account.
or
Pass the context token to the app immediately via an URL-scheme by registering your app as a protocol handler, see more info here
Suggested way:
Send e-mail with link and context token, when user clicks link, save context token in cookie in safari, then redirect to appstore for app download.
When the user downloaded the app and opens it, present a button for the user, when the user clicks it, open a web page in safari.
Safari loads the cookie with the context token, and then triggers another link using a URL-scheme like yourAppName://contextToken=12345678. The link opens your app which reads the context token from the URL.
There is no best practice for directly sharing data between safari and a native app directly and that it is simply not intended that you should do that. All cookies and storages are sandboxed for each app and safari has its own sandbox.
Letting your server doing the job via user accounts is the best and clean way i.m.o. That is why you have user accounts. If you didn't try out the protocol handler for reading specific URLs, that could also be made handy I think.
Could you have the app hit a URL on first launch hosted by server which is redirecting the user in safari, and compare IP addresses, time, iOS version, etc to get at least an approximate match? If an approximate match is insufficient, you could, when you see an approximate match, have your app open safari to confirm their identify via cookie.
It’s easy to send messages between a UIWebView and your native up using WebViewJavascriptBridge.
In your case, though, the accepted answer’s suggestion of using a custom URL scheme (directly from email to app, post-install) makes the most sense.