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.
Related
I need to resume my WebService if my bearer token expire by http code 401 ,
below is my code.
When a 401 error occurs that means the Access Token has expired and I need to send a Refresh Token over to my server to obtain a new Access Token.
After getting a new Access Token I need to redo the previous request that got the 401 error.
Suppose I hit webservice1 and its give 401 http code , then new bearer token request will generate and the same API resume to work. How can I do it ?
import UIKit
import CryptoKit
class SharedWebService: NSObject {
static let sharedApiInstance = SharedWebService()
func generalApiMethod (parameter : NSDictionary ,completion: #escaping ((NSMutableArray?) -> Void))
{
var dashboarddata : NSMutableArray = NSMutableArray()
let urlString = String(format:"URL OF API HERE")
let url = URL(string: urlString)
guard let requestUrl = url else { fatalError() }
var request = URLRequest(url: requestUrl)
request.timeoutInterval = 60
let bearerToken = "current bearer token"
request.setValue(bearerToken, forHTTPHeaderField: "Authorization")
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
URLSession.shared.dataTask(with: request) {
(data, response, error) in
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
if httpResponse.statusCode == 401
{
// Refresh bearerToken get here
let bearerToken = self.getBearerTokenDevice() //fetch api to get new bearer token
return
}
}
guard error == nil else {
print(error!.localizedDescription);
DispatchQueue.main.async{
completion(dashboarddata)
}
return
}
guard let jsonData = data else { print("Empty data"); return }
if String(data: jsonData, encoding: String.Encoding.utf8) != nil
{
do {
let responseJSON = try? JSONSerialization.jsonObject(with: jsonData, options: [])
if let responseJSON = responseJSON as? [String: Any] {
//Success case do here reponse return
completion(dashboarddata)
}
}
}
}.resume()
}
func getBearerTokenDevice()-> String
{
//how to handle it
return "New bearer token from api fetch"
}
}
work arround is,
Always call Api at splah which fetches Bearer token from server, it will refresh the token every time user opens the app,
2.1 make Api call Queue to process Api calls (Use generics here)
2.2 if api is successfull, Ok. if not than call a special Api call yo get the token,
2.3 if after fetching the token, get last api from Api Queue and call it..
its just an idea, how i think, i think it will be done, might be different in your case
https://stevenpcurtis.medium.com/use-operationqueue-to-chain-api-calls-in-swift-71eefd6891ef
here is guide to make Api call chain
I am currently in the process of reverse engineering a home automation API. I want to manage all settings with my own app - because there is really no current home automation app of the company.
Anyway - I already managed the authentication with my SmartHome device. To not make it too complicated: I need http digest authentication for final communication.
I have already been able to connect to my device through the command line with curl - unfortunately this doesn't work in Swift as planned.
curl -X POST -d '{"key": "value"}' https://192.168.0.0:1/action -k -s --digest --user username:password
Translated to Swift:
(1) Using Alamofire
import Alamofire
let data: [String: any] = ["key": "value"]
let request = Alamofire.request("https://192.168.0.0:1/action", method: HTTPMethod.post, parameters: data);
request.authenticate(user: "username", password: "password")
request.responseJSON { response in
// leads to error because of invalid self signed certificate of the smart home device ("https:")
}
Note to Alamofire: I guess using an external libary such as AF does not make much sense in this case - there are some unresolved issues that wont let such code as above work. (Self signed ceritficates makes problems, using custom manager instances overriding internal stuff leads also to problems) - I've already spent hours believe me.
(2) Using not Alamofire :)
extension ViewController: URLSessionDelegate {
public func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
let urlCredential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
completionHandler(.useCredential, urlCredential)
}
}
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
do {
let jsonData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
request.httpBody = jsonData;
let task = session.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
// success
}
}
task.resume()
} catch { }
The code above seems to work fine - the problem is that I've not implemented the digest authentication yet - because I do not find any method how to do this.
It would be super helpful if somebody to get some tips how generate the Auth header based on username and password
Edit
Curl uses this Authorization header:
> Digest username="username",
realm="XTV",
nonce="MTU5MDcNc2UxNjQ3OTo1YzMwYjc3YjIxMzAAAGQ5Nzg2NzUzMmRkZGU1ZVVlYw==",
uri="/action",
cnonce="MTExOTZlZmI1MjBlYWU0MTIzMDBmNDE0YTkWzJl1MDk=",
nc=00000001,
qop=auth,
response="2ba89269645e2aa24ac6f117d85e190c",
algorithm="MD5"
Is there the possibility to generate this header in Swift?
Digest authentication is supported automatically by URLSession and Alamofire through URLCredential (which is what authenticate() uses in Alamofire) when the server properly returns the WWW-Authenticate header with the proper digest settings.
You can generate the header manually, though I wouldn't recommend it due to the complexity of the digest process. I've found the wikipedia page to be thorough enough to implement the standard manually.
Currently learning how to add OAuth2 via Alamofire and getting confused. I'm using the password grant type and when a request fails I understand that the retrier kicks in and requests a token refresh. The confusing part is which one do I use?
Alamofire 4 using p2/OAuth2
Alamofire RequestRetrier + Request Adapter
The first one uses less code so unsure if its all the functionality I need. I also can't find a concrete example explaining this process.
I believe the following performs the refresh request?
private func refreshTokens(completion: RefreshCompletion) {
guard !isRefreshing else { return }
isRefreshing = true
let urlString = "\(baseURLString)/oauth2/token"
let parameters: [String: Any] = [
"access_token": accessToken,
"refresh_token": refreshToken,
"client_id": clientID,
"grant_type": "refresh_token"
]
sessionManager.request(urlString, withMethod: .post, parameters: parameters, encoding: .json).responseJSON { [weak self] response in
guard let strongSelf = self else { return }
if let json = response.result.value as? [String: String] {
completion(true, json["access_token"], json["refresh_token"])
} else {
completion(false, nil, nil)
}
strongSelf.isRefreshing = false
}
}
This is then passed back to adapt the previous request?
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: #escaping RequestRetryCompletion) {}
Is this the correct way of implementing this?
Thanks
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
First of all I created an account at https://www.fitbit.com
Then I careated an app at https://dev.fitbit.com
then installed OAuthSwift using cocoa pods and implemented this method in my AppDelegate
func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
if (url.host == "oauth-callback") {
OAuthSwift.handleOpenURL(url)
}
return true
}
now i want to get the data (Name, Steps taken etc) of user account I created at https://www.fitbit.com
how can I do that ? I searched but was not able to find any tutorial on fitbit integration. And where to use this information in my code?
So please guide me about next step what should I do to get the data.
FitBit work with OAuth 2.0 API which require Client ID and Secret key. You need these client id and secret key to authenticate with OAuth 2.0 API.
There is a blog post related to FitBit integration in iOS with Swift.
Lets checkout and learn "How to implement fitbit in iOS"
https://appengineer.in/2016/04/30/fitbit-aouth-in-ios-app/
ex:
let oauthswift = OAuth2Swift(
consumerKey: fitbit_clientID,
consumerSecret: fitbit_consumer_secret,
authorizeUrl: "https://www.fitbit.com/oauth2/authorize",
accessTokenUrl: "https://api.fitbit.com/oauth2/token",
responseType: "token"
)
Any chance you could do it with basic auth as opposed to OAuth? I had a similar problem trying to POST to MailGun for some automated emails I was implementing in an app.
I was able to get this working properly with a large HTTP response. I put the full path into Keys.plist so that I can upload my code to github and broke out some of the arguments into variables so I can have them programmatically set later down the road.
// Email the FBO with desired information
// Parse our Keys.plist so we can use our path
var keys: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("Keys", ofType: "plist") {
keys = NSDictionary(contentsOfFile: path)
}
if let dict = keys {
// variablize our https path with API key, recipient and message text
let mailgunAPIPath = dict["mailgunAPIPath"] as? String
let emailRecipient = "bar#foo.com"
let emailMessage = "Testing%20email%20sender%20variables"
// Create a session and fill it with our request
let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: NSURL(string: mailgunAPIPath! + "from=FBOGo%20Reservation%20%3Cscheduler#<my domain>.com%3E&to=reservations#<my domain>.com&to=\(emailRecipient)&subject=A%20New%20Reservation%21&text=\(emailMessage)")!)
// POST and report back with any errors and response codes
request.HTTPMethod = "POST"
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let response = response {
print("url = \(response.URL!)")
print("response = \(response)")
let httpResponse = response as! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
}
The Mailgun Path is in Keys.plist as a string called mailgunAPIPath with the value:
https://API:key-<my key>#api.mailgun.net/v3/<my domain>.com/messages?
Hope this helps!