AppAuth-iOS icon indicating copy to clipboard operation
AppAuth-iOS copied to clipboard

How to keep browser from showing sign in screen on every launch?

Open benstahl opened this issue 4 years ago • 1 comments

Hello, I'm using AppAuth on macOS (11.2.3) in a native Swift application and it works in terms of logging into Google and using the APIs. However, every time I launch the app, it asks me to re-authenticate in the browser (in this case Safari, but I have tried it with Chrome as well), even if I just used it minutes ago. I'm using it only with a single Google account on a single machine.

Is there a way I should be using a stored refresh token on the next launch to tell AppAuth to bypass the browser authentication and just use the saved token (if it's still valid)? I can save it in NSUserDefaults and/or iCloud Keychain but I don't know how to tell AppAuth about it.

Code that I call when app launches:

func authorizeGoogleAccount() -> Bool {
	let dg = DispatchGroup()
	dg.enter()

	let serverPort: UInt16 = 62626
	let issuer = URL(string: "https://accounts.google.com")!

	// discovers endpoints
	OIDAuthorizationService.discoverConfiguration(forIssuer: issuer) { configuration, error in
		guard let config = configuration else {
			print("Error retrieving discovery document: \(error?.localizedDescription ?? "Unknown error")")
			return
		}

		let kSuccessUrlString = "http://openid.github.io/AppAuth-iOS/redirect/"
		let url = URL(string: kSuccessUrlString)

		// Starts a loopback HTTP redirect listener to receive the code.  This needs to be started first,
		// as the exact redirect URI (including port) must be passed in the authorization request.
		self.redirectHandler = OIDRedirectHTTPHandler(successURL: url)
		let errPtr = NSErrorPointer(nilLiteral: ())
		guard let redirectUri = self.redirectHandler?.startHTTPListener(errPtr, withPort: serverPort) else {
			print("Failed to start HTTP auth listener.")
			return
		}

		// Configure the auth request.
		let request = OIDAuthorizationRequest(configuration: config, clientId: kGoogleClientID, clientSecret: kGoogleClientSecret, scopes: [kGoogleDriveAuthScope, kGoogleMailAuthScope], redirectURL: redirectUri, responseType: OIDResponseTypeCode, additionalParameters: nil)

		// Perform the auth request.
		self.redirectHandler?.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request) { authState, error in
			NSRunningApplication.current.activate(options: [.activateAllWindows, .activateIgnoringOtherApps])

			if let noptAuthState = authState, let token = noptAuthState.lastTokenResponse?.accessToken {
				self.googleAuthState = noptAuthState
				self.googleAuthAccessToken = token
				print("Google auth access token = \(token)")

				if let refreshToken = noptAuthState.lastTokenResponse?.refreshToken {
					self.googleAuthRefreshToken = refreshToken
					print("Google auth refresh token = \(refreshToken)")
					kKeychain.set(refreshToken, forKey: kGoogleKeychainItemName, withAccess: .accessibleAfterFirstUnlockThisDeviceOnly)
				} else {
					print("No refresh token returned.")
					_ = self.freshGoogleAccessToken()
				}
			} else {
				print("Auth error = \(error?.localizedDescription ?? "<nil>")")
			}

			dg.leave()
		}
	}

	dg.wait()

	return self.googleAuthAccessToken != nil
}

benstahl avatar Mar 20 '21 00:03 benstahl

As per the OpenID standard, adding this query param: ‘?prompt=login’ to the URL should present the login screen every single time irrespective of the session's availability.

saru2020 avatar Nov 01 '21 14:11 saru2020