I'm using the AppAuth library to get an access token for the Gmail API. I've successfully been able to create an Auth Session, and use the retrieved token to later fetch the emails.
In my AppDelegate I have two variables:
var currentAuthorizationFlow: OIDExternalUserAgentSession?
var authState: OIDAuthState?
In my SignInViewController, I have the following code for performing the authorization flow:
#objc func startAuthFlow() {
Analytics.logEvent("auth_started", parameters: nil)
let authorizationEndpoint = URL(string: "https://accounts.google.com/o/oauth2/v2/auth")!
let tokenEndpoint = URL(string: "https://www.googleapis.com/oauth2/v4/token")!
let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint,
tokenEndpoint: tokenEndpoint)
let kRedirectURI: String = "com.googleusercontent.apps.someNumber:/oauthredirect";
guard let redirectURI = URL(string: kRedirectURI) else {
return
}
let appDelegate = UIApplication.shared.delegate as! AppDelegate
// builds authentication request
let request = OIDAuthorizationRequest(configuration: configuration,
clientId: "myID",
clientSecret: nil,
scopes: ["https://mail.google.com/"],
redirectURL: redirectURI,
responseType: OIDResponseTypeCode,
additionalParameters: nil)
// performs authentication request
print("Initiating authorization request with scope: \(request.scope ?? "nil")")
appDelegate.currentAuthorizationFlow =
OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in
if let authState = authState {
print("Got authorization tokens. Access token: " +
"\(authState.lastTokenResponse?.accessToken ?? "nil")")
A0SimpleKeychain().setString((authState.lastTokenResponse?.accessToken)!, forKey: "auth0-user-jwt")
A0SimpleKeychain().setString((authState.lastTokenResponse?.refreshToken)!, forKey: "auth0-user-jwt-refresh")
EmailFetcher.shared.setupEmailSession(token: (authState.lastTokenResponse?.accessToken)!)
} else {
print("Authorization error: \(error?.localizedDescription ?? "Unknown error")")
}
}
}
I then successfully save the token and the refresh token.
I saw that there was a tokenRefreshRequest() method for OIDAuthState, but my understanding is that you would need to pass in the refresh token to get a new, fresh token, correct? What's the missing piece to implementing this with AppAuth?
To refresh an access token you use a 'refresh token grant' message with arguments similar to this:
let request = OIDTokenRequest(
configuration: self.metadata!,
grantType: OIDGrantTypeRefreshToken,
authorizationCode: nil,
redirectURL: nil,
clientID: self.configuration.clientId,
clientSecret: nil,
scope: nil,
refreshToken: tokenData!.refreshToken!,
codeVerifier: nil,
additionalParameters: nil)
OIDAuthorizationService.perform(request) { tokenResponse, error in ...
Note that I'm not using the AuthState class in my sample, since I wanted to store tokens encrypted in the iOS keychain, so your code may be a little different. For something to compare against, you can run the code sample from by blog:
Code Sample
Blog Post
Create an authState from auth token or directly save authstate model into key chain then access latest refresh token using below methods.
authState
authState.performAction { (accessToken, authToken, error) in
guard let err = error else {return}
print("updated refresh token is : \(accessToken)")
}
Related
I am trying to login using identity server but it showing 404 page after authentication
Here is the swift code i tried
let issuer = URL(string: "https://iq.app.com/coreidentityserver")!
// 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 request = OIDAuthorizationRequest.init(configuration: config,
clientId: "1111-1111-1111-1111-1111111",
clientSecret: "11111-1111-1111-1111-11111111",
scopes: ["openid", "profile"],
redirectURL: URL(string: "https://com.app.mobile/auth.html")!,
responseType: OIDResponseTypeCode,
additionalParameters: nil)
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
appDelegate.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self) { authState, error in
print("auth state",authState)
print("auth error",error)
}
print("items")
// perform the auth request...
}
I am using the Fitbit API to authorize my app and it is working fine however in the OAuthSwift documentation I don't see a way to set the expiration date of the generated token even tho the Fitbit API has a method to do so and the Implicit Grant Flow lets you set the expiration date to up to a year.
Here is my config for oAuthSwift:
let config = OAuth2Swift(
consumerKey: Constants.FitbitApi.consumerKey,
consumerSecret: Constants.FitbitApi.consumerSecret,
authorizeUrl: Constants.FitbitApi.authorizeUrl,
accessTokenUrl: Constants.FitbitApi.accessTokenUrl,
responseType: Constants.FitbitApi.responseType
)
Per de documentations those are the parameters it takes but no expiration date parameter. And here is my authorization method:
#IBAction func doOAuthFitbit(sender: AnyObject) {
let oauthswift = FitbitOAuth.sharedInstance.config
oauthswift.accessTokenBasicAuthentification = true
FitbitOAuth.sharedInstance.oauthswift = oauthswift
let state = generateState(withLength: 20)
oauthswift.authorizeURLHandler = SafariURLHandler(viewController: self, oauthSwift: oauthswift)
guard let callbackURL = URL(string: Constants.FitbitApi.callBackUrl) else { return }
let _ = oauthswift.authorize(withCallbackURL: callbackURL, scope: "weight", state: state) { result in
switch result {
case .success(let (credential, response, parameters)):
if let resp = response {
print(resp)
}
print(parameters)
print(credential.oauthToken)
FitbitOAuth.sharedInstance.saveOAuthInStorage()
self.authorizationLabel.text = "Authorized"
case .failure(let error):
print(error.localizedDescription)
self.authorizationLabel.text = "Not Authorized"
}
}
}
As noted this is working fine however the token always comes with an expiration date of 277895 and I would like to set it to 30 days or a year.
Thanks in advance.
I built a AppAuth test app for android using an Azure AD tenant and it works ok. Now I am trying to the same with iOS (Swift 4) and failing when trying to exchange an access code for access token. No error is returned, I do get an idToken but no accessToken or refreshToken. No other errors. Not sure what is going on. Without an access token I can't query the graph. I am using Azure AD v2. Here are some pieces of my code:
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: [OIDScopeOpenID, OIDScopeProfile], 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 code: \(String(describing: self.authState?.lastAuthorizationResponse.authorizationCode))")
self.doTokenRequest()
} else {
self.authState = nil
self.logMessage(message: "Authorization error: \(String(describing: error?.localizedDescription))")
}
})
}
func doTokenRequest() {
let tokenExchangeRequest = authState?.lastAuthorizationResponse.tokenExchangeRequest()
OIDAuthorizationService.perform(tokenExchangeRequest!) {
tokenResponse, error in
if tokenResponse == nil{
self.logMessage(message: "Token exchange error: \(error!.localizedDescription)")
} else {
self.authState?.update(with: tokenResponse!, error: error)
self.saveState()
self.logMessage(message: "Received token response with accesToken: \(tokenResponse!.idToken!)")
self.logMessage(message: "Received token response with accesToken: \(tokenResponse!.refreshToken!)")
self.logMessage(message: "Received token response with accesToken: \(tokenResponse!.accessToken!)")
self.retrieveUserProfile()
}
self.authState?.update(with: tokenResponse, error: error)
}
}
Got the answer. The problem is that depending on the authorization server, one has to use scopes defined for that server. In the code above, I used the default OpenId scopes of OIDScopeOpenID and OIDScopeProfile. As soon as I changed this to Azure AD scope of User.Read, everything started working correctly. So here is the net change to the code in function appAuthAuthorize:
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: ["User.Read"], redirectURL: NSURL(string: authConfig.redirectUri)! as URL, responseType: OIDResponseTypeCode, additionalParameters: nil)
doAppAuthAuthorization(authRequest: request)
}
I am working on an iOS mobile application where I need to upload videos using the Youtube Data API. In general, I understand the workflow needed:
1) Send a request to an authentication endpoint using clientId, clientSecret, and other details.
2) The service authenticates the client, and sends back a request to a client-specified callbackURL containing the access token.
3) The client provides the token in the header whenever he/she wants sends future requests.
I've successfully uploaded Youtube videos using node.js script, but I'm having a lot of trouble understanding how to implement this in Swift. In my VideoManagementController, I have a button that triggers the upload of a sample.mp4 file:
let headers = ["Authorization": "Bearer \(self.newToken))"]
let path = Bundle.main.path(forResource: "sample", ofType: ".mp4")
let videodata : NSData = NSData.dataWithContentsOfMappedFile(path!)! as! NSData
print("TOKEN: \(String(describing: token))")
Alamofire.request("https://www.googleapis.com/upload/youtube/v3/videos?part=id", method: .post, parameters: nil, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
print(response)
}
I am attempting to retrieve my access token in the viewDidLoad() stage of the controller:
let authorizationEndpoint = URL(string: "https://accounts.google.com/o/oauth2/v2/auth")
let tokenEndpoint = URL(string: "https://www.googleapis.com/oauth2/v4/token")
let configuration = OIDServiceConfiguration(authorizationEndpoint: authorizationEndpoint!, tokenEndpoint: tokenEndpoint!)
let request = OIDAuthorizationRequest(configuration: configuration, clientId: self.kClientID, scopes: [OIDScopeOpenID, OIDScopeProfile], redirectURL: self.KRedirectURI!, responseType: OIDResponseTypeCode, additionalParameters: nil)
let appDelegate: AppDelegate? = (UIApplication.shared.delegate as? AppDelegate)
print("REACHED")
appDelegate?.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self, callback: {(_ authState: OIDAuthState?, _ error: Error?) -> Void in
if (authState != nil) {
print("TOKEN: Got authorization tokens. Access token: \(String(describing: authState?.lastTokenResponse?.accessToken))")
self.newToken = authState?.lastTokenResponse?.accessToken
self.authState = authState
}
else {
print("TOKEN: Authorization error: \(String(describing: error?.localizedDescription))")
self.authState = nil
}
})
The issue is that my access token retrieval code essentially hangs and never completes. It reaches the print statement "REACHED" but never comes out of this following portion of code:
appDelegate?.currentAuthorizationFlow = OIDAuthState.authState(byPresenting: request, presenting: self, callback: {(_ authState: OIDAuthState?, _ error: Error?) -> Void in
if (authState != nil) {
print("TOKEN: Got authorization tokens. Access token: \(String(describing: authState?.lastTokenResponse?.accessToken))")
self.newToken = authState?.lastTokenResponse?.accessToken
}
else {
print("TOKEN: Authorization error: \(String(describing: error?.localizedDescription))")
self.authState = nil
}
I do not either get a new token or get the authorization error print out.
I suspect it has something to do with my callbackURL. I don't think Swift knows where to "listen" to get my access token from. A callbackURL endpoint is relatively easy to set up server-side in node.js, for example, but how do I do this in Swift? Or is this even the real issue?
i have tired in my code like this way , you also need to check sometime encoding type URLEncoding.httpBody hope it may help , it helps me in using through oauth
let headers = [
"Content-Type": "application/x-www-form-urlencoded"
]
Alamofire.request("https://accounts.google.com/o/oauth2/revoke?token={\(token)}", method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers).responseJSON { (response:DataResponse<Any>) in
How to get data from API with Oauth1? I just tried like this but it did not work.
import UIKit
import OAuthSwift
class TestLogin: UIViewController {
var oauthswift: OAuthSwift?
final let urlString = "https://conversation.8villages.com/1.0/contents/articles"
override func viewDidLoad() {
super.viewDidLoad()
self.doOAuth()
}
func doOAuth()
{
let oauthswift = OAuth1Swift(
consumerKey: "******",
consumerSecret: "******",
requestTokenUrl: "https://oauth.8villages.com/tokens/request-token",
authorizeUrl: "https://accounts.8villages.com/oauth/request-token",
accessTokenUrl: "https://accounts.8villages.com/oauth/access-token"
)
oauthswift.authorize(
withCallbackURL: URL(string: "https://8villages.com")!,
success: { credential, response, parameters in
print(credential.oauthToken)
print(credential.oauthTokenSecret)
print(parameters["userId"])
},
failure: { error in
print(error.localizedDescription)
}
)
}
func getHandleURL () {
let url = NSURL(string: urlString)
URLSession.shared.dataTask(with: (url as? URL)!, completionHandler: { (data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(jsonObj!.value(forKey: "data"))
}
}).resume()
}
}
so, how must I do or I need a reference example get data from API with Oauth1? I just don't know how to start to build project with OAuth because I search in google, only tutorial OAuth for login with social media.
In order to send oAuth 1.0 request basically you need to calculate proper query string and body parameter which actually based on your server implementation.
You need to get following query param:
oauth_consumer_key
oauth_nonce
oauth_signature_method
oauth_timestamp
oauth_version
You can check this blog where all the params are explained in very good detail and also the signature process. Also this answer guide you how to create HMAC-SHA1 signature in iOS
In the end of this process you need to create signature based on signature method which your app and server both agreed upon.
Then a sample POST request should look like following: Which is taken from oAuth1 guide
POST /wp-json/wp/v2/posts
Host: example.com
Authorization: OAuth
oauth_consumer_key="key"
oauth_token="token"
oauth_signature_method="HMAC-SHA1"
oauth_timestamp="123456789",
oauth_nonce="nonce",
oauth_signature="..."
{
"title": "Hello World!"
}
Hope it helps.