I'm using Alamofire to do a simple request
Alamofire.request(.GET, URL)
.authenticate(user: user, password: password)
.responseJSON { response in
...
}
After a first valid request, I changed the credential with invalid ones and the request succeed, but it should fail.
How can I invalidate previous credentials?
After a successful request, if I change the credential, Alamofire authenticates the previous credential.
How can I invalidate previous credentials?
I experienced exactly the same thing. iOS's answer should work, anyway I will provide code snippet using swift3 for someone to reduce research time.
let user = "aaaaa"
let password = "myPassword"
let plainString = "\(user):\(password)"
let plainData = plainString.data(using: .utf8)
let base64String = plainData?.base64EncodedString(options:NSData.Base64EncodingOptions(rawValue: 0))
let authString = "Basic " + base64String!
// Set in in Authorize header like...
let headers: HTTPHeaders = [
"Authorization": authString,
"Accept": "application/json"
]
// And use it like this..
Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}
Here is where this case was discussed >> https://github.com/Alamofire/Alamofire/issues/32
Adding Authorization header helped me.
let user = "user"
let password = "password"
let credentialData = "\(user):\(password)".dataUsingEncoding(NSUTF8StringEncoding)!
let base64Credentials = credentialData.base64EncodedStringWithOptions([])
let headers = ["Authorization": "Basic \(base64Credentials)"]
Alamofire.request(.GET, "https://httpbin.org/basic-auth/user/password", headers: headers)
.responseJSON { response in
debugPrint(response)
}
Related
I need to make a request to my own API, and the backend guy requires basic authentication when making request and also I have to put the token in the header. I am using Alamofire when making request to the API.
here is the basic authentication in postman
and here is the X-API-KEY token in the header
but I don't know how to implement both of basic auth and X-API-KEY token in the header. previously I can send request if just using basic authentication using the code below, But i have no idea how to make a request if both of them (Basic authentication and X-API-KEY token in the header) are required when making request using alamofire;
let urlSendDefect = URLService.defects.endPoint
let username = "admin"
let password = "1234"
var headers: HTTPHeaders = [:]
if let authorizationHeader = Request.authorizationHeader(user: username, password: password) {
headers[authorizationHeader.key] = authorizationHeader.value
}
let parameters : [String:Any] = ["defect_id": defectID, "defect_comment" : comment, "status" : status]
Alamofire.request(urlSendDefect,
method: .put,
parameters: parameters,
encoding: URLEncoding.default,
headers:headers)
.validate()
.responseJSON { response in
switch response.result {
case .failure(let error) :
print("Error while making request to send defect comment to server: \(error.localizedDescription)")
completion(nil,error)
case .success(let value) :
let json = JSON(value)
if let message = json["message"].string {
if message.isEmpty {
completion(nil,nil)
} else {
completion(message,nil)
}
} else {
completion(nil,nil)
}
}
}
Can you try to construct it like this
let headers : HTTPHeaders = ["Content-Type":"application/json",
"Authorization":"Basic \(Your_token)"]
and add any other key-value that you want
These are request headers:
let headers: HTTPHeaders = [
"Accept": "application/json",
"username": "someUserName",
"password": "aPasswordForSomeUserName"
]
When making a request with below code it's giving me "Garbage at the end". However, when I checked the response with JSON parser online. It's a valid JSON.
Alamofire.request("http://myserver/list.svc/random", headers: headers).responseJSON { response in
print(response)
}
I also tried making a request like this:
Alamofire.request("http://myserver/list.svc/random", headers: headers).responseString { response in
print(response)
}
I am getting this message in console: "401 UNAUTHORIZED".
What am I doing wrong? I believe, when using responseJSON completion block it's not complaining about Unauthorization, but it's complaining about bad JSON (or some garbage).
P.S. The same request works fine with Advance Rest Client (a chrome extension) and also in chrome browser.
I don't know how relevant this is to you anymore but I've got a working solution I'll post for any future reference.
So, I had two issues. The first one being that the Authorization header fell of the request when it was redirected. The second one being the NTLM-challenge from the server not being handled. The following code should be quite self explanatory I hope :) It asumes you store the username and password in variables name just that.
let credentialData = "\(username):\(password)".data(using: String.Encoding.utf8)!
let base64Credentials = credentialData.base64EncodedString(options: [])
request.addValue("Basic \(base64Credentials)", forHTTPHeaderField: "Authorization")
let manager = Alamofire.SessionManager.default
let delegate: Alamofire.SessionDelegate = manager.delegate
// This bit will re-add the auth headers for the redirected request
delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
var redirectedRequest = request
if let originalRequest = task.originalRequest, let redirectheaders = originalRequest.allHTTPHeaderFields {
if let authorizationHeaderValue = redirectheaders["Authorization"] {
redirectedRequest.setValue(authorizationHeaderValue, forHTTPHeaderField: "Authorization")
}
if let contentTypeHeaderValue = redirectheaders["Content-Type"] {
redirectedRequest.setValue(contentTypeHeaderValue, forHTTPHeaderField: "Content-Type")
}
}
return redirectedRequest
}
// This bit looks at challenges received and applies the correct credentials
delegate.taskDidReceiveChallenge = { session, task, challenge in
var disposition: URLSession.AuthChallengeDisposition = .useCredential
var credential: URLCredential = URLCredential()
if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodNTLM) {
disposition = URLSession.AuthChallengeDisposition.useCredential
credential = URLCredential(user: username, password: password, persistence: URLCredential.Persistence.forSession)
}
if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
disposition = URLSession.AuthChallengeDisposition.useCredential
credential = URLCredential(trust: challenge.protectionSpace.serverTrust!)
}
return(disposition, credential)
}
manager.request(request).responseData { (response) in
// Handle response accordingly
}
Hope this helps someone.
In Swift4.2
Alamofire has built in NTLM auth support.
You can make a request like that
let user = "YOUR_USER_NAME OR EMAIL"
let password = "YOUR_PASSWORD"
let url = "YOUR_API_URL"
let credential = URLCredential(user: user, password: password, persistence: .forSession)
//These headers are optional based on your api and your server.
//There were required for me
let headers = ["Accept": "application/json;odata=verbose",
"Content-type": "application/json;odata=verbose"]
Alamofire.request(url, method: .get, headers: headers).authenticate(usingCredential: credential).responseJSON {
(response) in
switch response.result {
case .success:
if let value = response.result.value {
print("The server response is: ", value)
}else{
print("There is error in the server response")
}
case .failure (let error):
print("The NTLM request error is: ", error.localizedDescription)
}
}
I'm having a problem on using Alamofire. When I try to post a request using a generic parameters like ["name":"John", "age":"27"] it always succeeds. But, when I try to use a web service that requires parameters and a body-raw for a base64 string I'm not able to get a successful response from the server. Though it succeeds when I use Postman. Does anyone knows how to do this on Alamofire 4? Here is the screenshot of my postman.
Thank you!
#nathan- this is the code that I used. I just assumed that the base64String inside the "let paramsDict" has a key value named "data" though it doesn't have a key name in postman.
let urlString = ApiManager.sharedInstance.formsURL + ApiManager.sharedInstance.mobileFormsImageUpload
let paramsDict = ["token": token, "fileID":"2", "filename":"images.png", "data": base64String] as [String : Any]
Alamofire.request(urlString, method: .post, parameters: paramsDict, encoding: URLEncoding.httpBody, headers: [:])
.responseJSON{ response in
switch response.result {
case .success(let data):
debugPrint("SUCCESS")
case .failure(let error):
debugPrint("Request Error")
}
}
I already figured it out. It needs a custom encoding to make it work. All the parameters must be inlined with the url so the base64 string inside the parameter is the only to be encoded. Here is the code that I used.
struct CustomPostEncoding: ParameterEncoding {
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var request = try URLEncoding().encode(urlRequest, with: parameters)
let base64 = parameters?["data"] as! String
let finalBase64Format = "\"" + base64 + "\""
let postData = NSData(data: finalBase64Format.data(using: String.Encoding.utf8)!)
request.httpBody = postData as Data
return request
}
}
func uploadImageBase64(){
let jpegCompressionQuality: CGFloat = 0.9 // Set this to whatever suits your purpose
if let base64String = UIImageJPEGRepresentation(testIMG, jpegCompressionQuality)?.base64EncodedString() {
var token = String()
if let data = UserDefaults.standard.data(forKey: "userProfile"),
let user = NSKeyedUnarchiver.unarchiveObject(with: data) as? UserProfile{
token = user.token
} else {
print("There is an issue")
}
let headers = [
"content-Type": "application/json"
]
let urlString = "http://localhost/FormsService.svc/Getbase64?filename=test.png&fileID=1151&token=80977580xxx"
let paramsDict = ["data": base64String] as [String : Any]
Alamofire.request(urlString, method: .post, parameters: paramsDict, encoding: CustomPostEncoding(), headers: headers)
.responseJSON{ response in
print("response JSON \(response.result)")
}
.response{ response in
print("RESPONSE \(response)")
}
}
}
I am using Alamofire to send a request to MailChimp to add a user to a list
MailChimp's docs say:
There are 2 authentication methods for the API: HTTP Basic authentication and OAuth2. The easiest way to authenticate is using HTTP Basic authentication. Enter any string as your username and supply your API Key as the password.
The request I wrote for Alamofire:
let params: [String : AnyObject] = ["email_address": email, "status": "subscribed", "merge_fields": [ "FNAME": name]]
guard let url = "https://us10.api.mailchimp.com/3.0/lists/<listID>/members/".stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) else { return }
Alamofire.request(.POST, url, parameters: params, encoding: .URL)
.authenticate(user: "apiKey", password: "<apikey>")
.responseJSON { response in
if response.result.isFailure {
}
else if let responseJSON = response.result.value as? [String: AnyObject] {
}
}
I checked that the API key is correct by using it to access their playground:
https://us1.api.mailchimp.com/playground/
The response I get back states that the API key was not included:
Your request did not include an API key.
Where have I gone wrong?
Swift 3
Make sure you take a look at MailChimp's Error Glossary. A 401 indicates that your API key is not being read properly.
For Swift 3, the header construction in Abbey Jackson's answer needs to be updated to this. Otherwise it totally works.
let credentialData = "AnyString:\(apiKey)".data(using: String.Encoding.utf8)!
let base64Credentials = credentialData.base64EncodedString()
let headers = ["Authorization": "Basic \(base64Credentials)"]
Here's an example that uses Request.authorizationHeader instead.
let apiKey: String = "xxxxxxxxxxxxxxx2b-us11" // note the 'us11'
let baseUrl: String = "https://us11.api.mailchimp.com/3.0" // note the 'us11'
let listId: String = "xxxxxx2f"
func createListMember() {
let url = "\(baseUrl)/lists/\(listId)/members"
guard let authorizationHeader = Request.authorizationHeader(user: "AnyString", password: apiKey) else {
print("!authorizationHeader")
return
}
let headers: HTTPHeaders = [
authorizationHeader.key: authorizationHeader.value
]
let parameters: Parameters = [
"email_address": email,
"status": "subscribed"
]
// perform request (make sure you're using JSONEncoding.default)
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers)
//.authenticate(user: "AnyString", password: apiKey) // this doesn't work
.validate()
.responseJSON {(response) in
print(response)
}
}
So MailChimp actually needs the api key sent in the authorization header like this:
let params: [String: AnyObject] = ["email_address": email, "status": "subscribed"]
guard let url = "https://us10.api.mailchimp.com/3.0/lists/<listID>/members/".stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) else { return }
let credentialData = "user:<apikey>".dataUsingEncoding(NSUTF8StringEncoding)!
let base64Credentials = credentialData.base64EncodedStringWithOptions([])
let headers = ["Authorization": "Basic \(base64Credentials)"]
Alamofire.request(.POST, url, headers: headers, parameters: params, encoding: .URL)
.responseJSON { response in
if response.result.isFailure {
}
else if let responseJSON = response.result.value as? [String: AnyObject] {
}
}
edit: See Derek Soike's answer below for Swift 3
If you are passing the apikey as like bxxxxxxxxxxxxxxxxxxxxxxxxxxxxx5dxf-us10, then you'll get the error as you specified.
Try to pass the apikey by skipping the characters after the hyphen (like "bxxxxxxxxxxxxxxxxxxxxxxxxxxxxx5dxf") and check if it works.
Swift 3 As of Sept. 5, 2017
It says method is an extra argument by accident. It's likely that your encoding part is off. It should look like this:
encoding: JSONEncoding.default
I keep getting "error_type":"OAuthException","code":"41" when I using alamofire or even when i made it through to the server I got data from before header's authorisation. I think it keep sending same header, how to make sure that alamofire send the current headers?
let headers = ["Authorization" : "\(AccessToken) \(TokenType)"]
print(headers)
Alamofire.request(.GET, "url/profile/", headers: headers, encoding: .JSON).responseJSON { response in
switch response.result {}
EDIT
First, I use login API
let parameters = [
"client_id": "\(Constant.clientId)",
"client_secret": "\(Constant.clientSecret)",
"response_type": "\(Constant.responseType)",
"scope" : "\(Constant.scope)",
"redirect_uri": "\(Constant.redirect)",
"email": "\(email!)",
"password": "\(pass!)"
]
print(parameters)
Alamofire.request(.POST, "http://url/login/", parameters: parameters, encoding: .JSON).responseJSON { response in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
print("JSON: \(json)")
let accessToken = json["access_token"].string!
let refreshToken = json["refresh_token"].string
let tokenType = json["token_type"].string!
let expiresIn = json["expires_in"].string
}
And then, I use accessToken and tokenType for authorization
if(refreshToken != nil)
{
let headersCust = ["Authorization" : "\(accessToken) \(tokenType)"]
print(headersCust)
Alamofire.request(.GET, "http://goodies.co.id/api/v1/customer/profile/", headers: headersCust, encoding: .JSON).responseJSON { response in {}
Usually this problem is caused by Redirection. Using some networking debugging tool like Proxyman helps you to understand if this is a case; if so :
this is Alamofire 5(AF 5) solution:
let headers: [String:String] = [...]
let params: [String: Any] = [...]
let url = URL(...)
let redirector = Redirector(behavior: Redirector.Behavior.modify({ (task, urlRequest, resp) in
var urlRequest = urlRequest
headers.forEach { header in
urlRequest.addValue(header.value, forHTTPHeaderField: header.key)
}
return urlRequest
}))
//use desired request func of alamofire and your desired enconding
AF.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers)
.responseJSON { response in
//handleDataResponse...
}.redirect(using: redirector)
I hope you are using the latest Alamofire so here is the code for .GET Request:
let headers = [
"Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"Content-Type": "application/x-www-form-urlencoded"
]
Alamofire.request(.GET, "https://httpbin.org/get", headers: headers)
.responseJSON { response in
debugPrint(response)
}
Reference: https://github.com/Alamofire/Alamofire
Try this out! And make sure you are sending the proper headers.