OAuth Error General Code 4 Swift - AppAuth for iOS - ios

Im trying to implement AppAuth in iOS. basic implementation has been done. seems to be everything working fine. but im not recieving the token as expected. im getting Error Error Domain=org.openid.appauth.general Code=-4
let authorizationEndpoint : NSURL = NSURL(string: "https://accounts.google.com/o/oauth2/v2/auth")!
let tokenEndpoint : NSURL = NSURL(string: "https://www.googleapis.com/oauth2/v4/token")!
let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint as URL, tokenEndpoint: tokenEndpoint as URL)
let request = OIDAuthorizationRequest.init(configuration: configuration, clientId: "<MyTOKEN>", scopes: [OIDScopeOpenID], redirectURL: URL(string: "http://127.0.0.1:9004")!, responseType: OIDResponseTypeCode, additionalParameters: nil)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
// appDelegate.currentAuthorizationFlow
appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self, callback: { (authState, error) in
if((authState) != nil){
print("Got authorization tokens. Access token: \(authState?.lastTokenResponse?.accessToken)")
}else{
print("Authorization error \(error?.localizedDescription)")
}
})

After dealing with errors and changes i figured out the problem after dealing with redirectUri and Token.
redirectUri - once you authorized with google it will generate the token and after that you should open the app. redirectUri will help with that.
This is how you can setup redirectUri
The value for iOS URL scheme wil be the scheme of your redirect URI. This is the Client ID in reverse domain name notation, e.g. com.googleusercontent.apps.IDENTIFIER. To construct the redirect URI, add your own path component. E.g. com.googleusercontent.apps.IDENTIFIER:/oauth2redirect/google. Note that there is only a single slash (/) after the scheme.

Related

Callback URL not calling completionHandler

I’m trying to use ASWebAuthentication to facilitate the authentication process with Oauth 1.0. After the
user enters their credentials and approves my application, oauth provider uses the redirect_url I passed in, com.me.appName:/returnToApp
and the Safari window looks something like this:
Here's my code:
func getAuthTokenWithWebLogin(context: ASWebAuthenticationPresentationContextProviding) {
let callbackUrlScheme = “scheme:/returnToApp"
let authURL = URL(string: Constants.authURL)
guard authURL != nil else{return}
let webAuthSession = ASWebAuthenticationSession.init(url: authURL!, callbackURLScheme: callbackUrlScheme, completionHandler: { (callBack:URL?, error:Error?) in
// handle auth response
guard error == nil, let successURL = callBack else {
return
}
let oauthToken = NSURLComponents(string: (successURL.absoluteString))?.queryItems?.filter({$0.name == "code"}).first
// Do what you now that you've got the token, or use the callBack URL
print(oauthToken ?? "No OAuth Token")
})
// New in iOS 13
webAuthSession.presentationContextProvider = context
// Kick it off
webAuthSession.start()
}
I don't think it's an issue with ASWebAuthentication, since I've had the same problem when I tried using third party library OauthSwift
My bad; I didn't realize that the callbackURL must be in myApp:// format, while I had mine as myApp:/ (single slash) thinking this would also work.

Basecamp3 login issue with Oauth2 in Swift

My app needs to get a basecamp3 login. Hence I used the OAuth2Swift library. But unfortunately, I am unable to receive the token from basecamp even the user has authorized the app.
Below is the screenshot
I have used the following code
func createAuthRequest(){
// create an instance and retain it
let oauthswift = OAuth2Swift(
consumerKey: clientID,
consumerSecret: clientSecret,
authorizeUrl: authURL,
responseType: "token"
)
//oauthswift.authorizeURLHandler = self
oauthswift.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauthswift)
let handle = oauthswift.authorize(
withCallbackURL: URL(string: redirectURL)!,
scope: "profile", state:"") { result in //This block of code never executed
switch result {
case .success(let (credential, response, parameters)):
print(credential.oauthToken)
// Do your request
case .failure(let error):
print(error.localizedDescription)
}
}
}
The code inside withCallbackURL never executed even the user has authorized the app.
Any help regarding this is appreciated.
I found the solution the problem was I was using wrong authentication & token URL.
Following URL need to be used. I missed to add web_server in auth/token url and unfortunately Basecamp haven't mentioned the same in their documets.
let authURL = "https://launchpad.37signals.com/authorization/new?type=web_server"
let tokenURL = "https://launchpad.37signals.com/authorization/token?type=web_server"
and redirectURL = com.abc.abc:/oauth2Callback (The same redircturl need to be updated for app under basecamp developer console where com.abc.abc is bundle id of the app)

AppAuth iOS. How to save the token info after user registers an account in my own OAuth2 server

I have an iOS app and a backend written in Spring with OAuth2 mechanizm implemented.
I have a signup endpoint in my backend, which accepts some user data and returns an OAuth2 response, e.g.
{
"access_token":"9154140d-b621-4391-9fdd-fbba9e5e4188",
"token_type":"bearer",
"refresh_token":"dd612036-7cc6-4858-a30f-c548fc2a823c",
"expires_in":89,
"scope":"write"
}
After that I want to save OIDAuthState in my keychain, so I do the following:
func saveTokenResponse(responseDict: [String: NSCopying & NSObjectProtocol]) {
let redirectURI = URL(string: kRedirectURI)
let configuration = OIDServiceConfiguration(authorizationEndpoint: URL(string: kAuthorizationEndpoint)!, tokenEndpoint: URL(string: kTokenEndpoint)!)
let request = OIDAuthorizationRequest(configuration: configuration, clientId: self.kClientID, scopes: ["write"], redirectURL: redirectURI!, responseType: OIDResponseTypeCode, additionalParameters: nil)
let accessToken = responseDict["access_token"] ?? nil
let tokenType = responseDict["token_type"] ?? nil
let refreshToken = responseDict["refresh_token"] ?? nil
let expiresIn = responseDict["expires_in"] ?? nil
let scope = responseDict["scope"] ?? nil
let response = OIDAuthorizationResponse(request: request, parameters: [
"access_token": accessToken!,
"token_type": tokenType!,
"refresh_token": refreshToken!,
"expires_in": expiresIn!,
"scope": scope!
]
)
let authState = OIDAuthState(authorizationResponse: response) //here authState.refreshToken is nil, so it won't be serialized.
updateAuthState(authState: authState) // it just saves it in keychain
}
Everything works well, but I have an issue when the token expires.
The app makes a call to the backend and AppAuth is not able to refresh the token:
I can see, that the refresh token is not present in the OIDAuthState object. I have checked the initialization of the OIDAuthState from OIDAuthorizationResponse and found out that token is not assigned in that case.
Can anybody help how I can save OIDAuthState from the OAuth response I receive from my backend? Or am I doing something in a wrong way?
Thanks,
Osman
If your token is expires then you move on login screen (call sign out function) like all app (Banking system, Amazon, Paytm and Facebook). So user when login with your credential then getting same responses like you want and need refresh token. You have get samilar information in login web service like below response. I hope it's work fine
let accessToken = responseDict["access_token"] ?? nil
let tokenType = responseDict["token_type"] ?? nil
let refreshToken = responseDict["refresh_token"] ?? nil
let expiresIn = responseDict["expires_in"] ?? nil
let scope = responseDict["scope"] ?? nil
let response = OIDAuthorizationResponse(request: request, parameters: [
"access_token": accessToken!,
"token_type": tokenType!,
"refresh_token": refreshToken!,
"expires_in": expiresIn!,
"scope": scope!]
)
I don't know what exactly you're doing but I would suggest you to use a different mechanism which is widely used.
Everytime a user logs in with Google, a temporary access token is sent by Google which expires in a short while.
The access token you get on logging in, should be sent to your server just to see and check if the user is authentic and all his personal details can be fetched along with it too (via GoogleAPI call).
Send this access token to your server as soon as the user logs in via Google. Autheticate this token on server-side.
After the access token is authenticated, send your own unique token from your server (with an expiry time or not) to the user.
The user continues app usage with this unique token you've provided.
I ran into this same issue using AppAuth for iOS (refreshToken was null). I was able to get it working by adding adding "offline_access" to my scopes.
let request = OIDAuthorizationRequest(configuration: configuration!, clientId: self.kClientID, scopes: [OIDScopeOpenID, OIDScopeProfile, "api", "offline_access"], redirectURL: redirectURI! as URL, responseType: OIDResponseTypeCode, additionalParameters: nil)
Change the let redirectURI = URL(string: kRedirectURI) string to lowercase, oAuth for ios is picky.
Could also try to use Okta as a middleman service that plugs right in with oAuth.

Athentication problems on iOS when using AppAuth and Okta

I have a simple iOS Swift app loosely based on the AppAuth-iOS example (https://github.com/openid/AppAuth-iOS) as well as Okta OAuth sample (https://github.com/oktadeveloper/okta-openidconnect-appauth-ios). I am not using Service Discovery nor authomatic token aquisition (i.e. not using authStateByPresentingAuthorizationRequest).
My sample works against Azure AD but does not work against Okta. I am able to log in and am authenticated and redirected back to my mobile app (AppDelegate.application()) but then the flow does not return to my OIDAuthorizationService.present() completion block.
Here is some code:
#IBAction func signInButton(_ sender: Any) {
// select idp
switch selectedIdentityProvider! {
case "Azure AD":
selectedAuthConfig = AzureAdAuthConfig()
case "Okta":
selectedAuthConfig = OktaAuthConfig();
default:
return
}
appAuthAuthorize(authConfig: selectedAuthConfig!)
}
func appAuthAuthorize(authConfig: AuthConfig) {
let serviceConfiguration = OIDServiceConfiguration(
authorizationEndpoint: NSURL(string: authConfig.authEndPoint)! as URL,
tokenEndpoint: NSURL(string: authConfig.tokenEndPoint)! as URL)
let request = OIDAuthorizationRequest(configuration: serviceConfiguration, clientId: authConfig.clientId, scopes: authConfig.scope, redirectURL: NSURL(string: authConfig.redirectUri)! as URL, responseType: OIDResponseTypeCode, additionalParameters: nil)
doAppAuthAuthorization(authRequest: request)
}
func doAppAuthAuthorization(authRequest: OIDAuthorizationRequest) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.currentAuthorizationFlow = OIDAuthorizationService.present(authRequest, presenting: self, callback: {
(authorizationResponse, error) in
if (authorizationResponse != nil) {
self.authState = OIDAuthState(authorizationResponse: authorizationResponse!)
self.logMessage(message: "Got authorization tokens. Access token: \(String(describing: self.authState?.lastAuthorizationResponse.authorizationCode))")
self.doTokenRequest()
} else {
self.authState = nil
self.logMessage(message: "Authorization error: \(String(describing: error?.localizedDescription))")
}
})
}
I could rewrite the code to use authStateByPresentingAuthorizationRequest() to see if it works but am a bit leery as this code works against Azure AD. Any suggestions?
Update 1
I forgot to mention that I have a working Android/Java example going against the same Okta definitions and working like a charm.
Update 2
I did rewrite the code to use authStateByPresentingAuthorizationRequest() against Okta and am getting the same result (i.e. getting stuck after redirect back to my app). I tested this against Azure AD and it works Ok.
Resolved. I guess the problem was that the redirect URL defined in Okta was mixed case. Android AppAuth implementation does not mind but iOS AppAuth implementation does. Changed redirect URL in Okta to lower case only, changed redirect Uri paramter passed in to lower case only and bing, all works great. Thanks #jmelberg for pointing me in this direction - by debugging resumeAuthorizationFlow(with: url) I was able to see the exact behaviour and why the call returned a False.

Outlook OAuth2 can't redirect to application

I'm currently trying use the outlook mail service to obtain contacts/appointments and mails. However I've stumbled upon a problem.
I'm using OAuth2Swift as library to make all my OAuth calls since I'm integrating multiple services.
I created a URL schemelike described in their README
Then I created a Constants file which looks like this
struct Consumer {
let consumerKey: String
let consumerSecret: String
let authorizeURL: String
let accessTokenURL: String
let responseType: String?
let requestTokenURL: String?
}
let Outlook = Consumer(
consumerKey: "",
consumerSecret: "",
authorizeURL: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
accessTokenURL: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
responseType: "code",
requestTokenURL: nil)
I created an application for outlook on https://apps.dev.microsoft.com
generated my key and secret and filled them in inside my application.
I added the mobile application platform to my app. Which tells me to use the redirect URI urn:ietf:wg:oauth:2.0:oob
So my code to authorise looks like this
#IBAction func btn_Outlook(_ sender: Any) {
let oauthOU = OAuth2Swift(
consumerKey: Outlook.consumerKey,
consumerSecret: Outlook.consumerSecret,
authorizeUrl: Outlook.authorizeURL,
accessTokenUrl: Outlook.accessTokenURL,
responseType: Outlook.responseType!)
oauthOU.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauthOU)
oauthOU.authorize(
withCallbackURL: "urn:ietf:wg:oauth:2.0:oob",
scope: "https://outlook.office.com/Mail.ReadWrite https://outlook.office.com/Mail.Send https://outlook.office.com/Calendars.ReadWrite https://outlook.office.com/Contacts.ReadWrite https://outlook.office.com/Tasks.ReadWrite",
state: state,
success: { credential, response, parameters in
print("logged in with \(credential), with response \(response) and parameters \(parameters)")},
failure: { error in
print("error occured \(error.localizedDescription)")
}
)
}
when I run the code I first get a screen to enter my mail/password. When I enter my mail it will redirect me to a different page/portal where I can enter my password. When I've entered my password it will show me the permissions screen.
as soon as I hit yes it will give me an error saying "Safari cannot open the page because the address is invalid." I'm pretty sure this has to do with the redirect URI but I'm not sure what to do to actually fix this.
I'm hoping someone is able to help me out with this!
I think you forgot to specify the identifier urn:ietf:wg:oauth:2.0:oob in your URL scheme settings (see first image: there is no identifier set)
I think you forgot to handle redirect URI in AppDelegate from OAuthSwift library! You've to handle the callback in AppDelegate as below.
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if (url.host == "oauth-callback") {
OAuthSwift.handle(url: url)
}
return true
}

Resources