Currently, I'm making an application that uses Django Rest Framework as my API and iOS (Swift) as my frontend using Alamofire for API calls. However, I've run into an issue with user authentication - whenever I try to make a POST request to login a user using Alamofire, I'm hit with this 403 error:
Here is my login setup with Alamofire:
func loginUser(data: Parameters) {
let finalUrl = self.generateUrl(addition: "auth/login/")
print(finalUrl)
let header: HTTPHeaders = [ "Accept": "application/json", "Content-Type" :"application/json"]
Alamofire.request(finalUrl,method: .post, parameters: data, encoding: JSONEncoding.default, headers: header).responseString { (response:DataResponse<String>) in
print(data)
switch(response.result) {
case .success(_):
if response.result.value != nil {
print(response.result.value!)
}
break
case .failure(_):
print(response.result.error!)
break
}
}
}
On the API side, the login I am using is the one provided by rest_framework.urls...
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework'))
While advice from similar posts has not resolved my issue, I believe my options are
a.) Exempt my views from requiring a CSRF token (I'm not sure if it's even possible in my case - my views are bundled with include() as part of the rest_framework.urls scheme so decorating with csrf_exempt cannot work)
b.)Obtain a CSRF Token for my POST requests somehow
While these are my ideas, I've yet to find an actual solution or method to implement them, so any help would be greatly appreciated!
Session Based Authentication is not required if you are building APIs for any mobile app. If you don't use cookies to manage your sessions, you don't need any CSRF protection. Am i wrong ? but anyway if you want to do so, pass #csrf_exempt
Instead of that it is better to use Token Based Authentication .You can check it here in the django -rest-api-docs . Token authentication is appropriate for client-server setups, such as native desktop and mobile clients.
Related
I'm testing an app not yet published. I have a client side register/log-in, using Alamofire to post and then retrieve and parse JSON. Unless Alamofire has "blackboxed" some type of hash, I am not aware of having coded any kind of hash anywhere, yet.
When I went to look into why the test passwords (passed via SSL, HTTPS, and without any effort to encrypt, yet) were showing up on the server side looking like the result of a hash, I compared that result to a deliberate server side Sha256 hash (done on the raw, plain text password matching the original that got passed from the app). I am seeing this:
"ccc" ----> Sha256 hash = 64daa44ad493ff28a96effab6e77f1732a3d97d83241581b37dbd70a7a4900fe
"ccc" ----> "simple iOS post" (via Alamofire) = 9df62e693988eb4e1e1444ece0578579
As you can see, the values are very different, and this means unless I know what happened on the way over, I cannot authenticate anyone on the server side, nor can I use any server side password reset functions, because I have no idea what kind of hash was used.
Can anyone help me know what happened to hash the password?
Here's the simple Alamofire-based code doing the post (Xcode 9, Swift 4):
//creating parameters for the post request
let parameters: Parameters=[
"username":textFieldUserName.text!,
"password":textFieldPassword.text!,
"name":textFieldName.text!,
"email":textFieldEmail.text!,
"phone":textFieldPhone.text!,
"user_type":String(user_type),
"user_privileges":String(user_privileges)
]
print("Post Contents ('parameters') = \(parameters)")
//Sending http post request
Alamofire.request(URL_USER_REGISTER, method: .post, parameters: parameters).responseJSON
{
response in
//printing response
print(response)
//getting the json value from the server
if let result = response.result.value {
//converting it as NSDictionary
let jsonData = result as! NSDictionary
}
}
Well, I feel sheepish. I found a hash being applied in the PHP on the server side.
md5($pass);
No need to bother with this one. Now wish I had not even posted it. But, maybe it will help someone.
I want to make a HTTP post request via Alamofire 4.5. The request need an authorization header(which is a Api key). But whenever I fired the request,my server cant detect the ApiKey.'
Here is how I make the Alamofire request
let params : [String : Any] =["param1":param1,"param2":param2]
let headers : HTTPHeaders = ["authorization" : apiKey]
Alamofire.request(MY_URL, method: .post, parameters: params, headers: headers).responseJSON {
response in
switch response.result{
case .success(let result):
//other code here
}
I triple checked the value of apiKey ,the value is correct,but the request sent,my server cant detect the authorization at all.
I totally no idea whether I do anything wrong here,cause I very new in Swift.Kindly provide a proper solution.Thanks
Edit :
In my server code,I using Slim 2
$app->map('/MY_URL','authenticate',function ()use($app){
}
'authenticate' is the point that scan for the authorization: apiKey in the headers,so now the problem is my server cant get the value of apiKey therefore always giving the same error "Api Key is missing" which I set when no Api Key found.
I tried the method below in Alamofire Documentation,but the result still the same.
What I tried:
let headers: HTTPHeaders = [
"Authorization": "Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==",
"Accept": "application/json"
]
Alamofire.request("https://httpbin.org/headers", headers: headers).responseJSON { response in
debugPrint(response)
}
What I missing here?Somebody please give me some hints to do it..Thank you.
EDIT:
To be more clear on my I mean for authorization : apiKey I show the way I make request in Postman.
Normally I just insert the "authorization": apiKey in the Headers in the request
but in Swift,the web service cant get the value of apiKey,therefore the server always return this following response :
{
"error": true,
"message": "Api key is missing"
}
This is working fine for me with Alamofire 4.6.0
let url = "WEB API URL"
let headers = [
"Content-Type":"application/x-www-form-urlencoded",
"authorization" : "apiKey"
]
let configuration = URLSessionConfiguration.default
configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
let params : [String : Any] = ["param1":param1,"param2":param2]
Alamofire.request(url, method: .post, parameters: params as? Parameters, encoding: URLEncoding.httpBody, headers: headers).responseJSON { response in
if let JSON = response.result.value {
print("JSON: \(JSON)")
}else{
print("Request failed with error: ",response.result.error ?? "Description not available :(")
}
}
TLDR;
The problem is that iOS's URLRequest automatically capitalize headers. At the same time you API does not follow best practices.
Change your API to comply to RFC 7230 and allow it to accept headers case-insensitively.
The whole story:
At first, your question seemed a bit odd since there is no obviously wrong code in what you provided. Nevertheless I tried to reproduce your request in Postman.
Now we should stop and I must warn you to never post what you did in your "Here is my request" section. The information given there allowed me to completely reproduce your request in Postman (including headers and exact fields' names and values), which is good to solve your problem. But at the same time you shared your presumably private and maybe even bought API key to everyone who see your question. Which is obviously not good and I would recommend you to change your API key if it is possible.
Then I tried your code and noticed exactly the same behavior you talking about. I debugged responseJSON closure and observed response.request?.allHTTPHeaderFields property:
(lldb) po response.request?.allHTTPHeaderFields
▿ Optional<Dictionary<String, String>>
▿ some : 2 elements
▿ 0 : 2 elements
- key : "Content-Type"
- value : "application/x-www-form-urlencoded; charset=utf-8"
▿ 1 : 2 elements
- key : "Authorization"
- value : "f8f99f9506d14f0590863d5883aaac9b"
(if you don't understand what I wrote read about debugging in xcode and in particular for lldb's po command)
As you can see, authorization header's name start with a capital A letter even though I passed it all lowercased.
I tried to send new request with postman with capital A and yes - I learned that your API accepts only lower-cased authorization header name.
"It isn't really a problem" you think right now. "We should just change our authorization header name somewhere and it should be just fine, right?"
NOT SO EASY.
I tried a few things which all lead me to the URLRequest's setValue(_:forHTTPHeaderField:) method. Alamofire calls it and I tried it too. Surprisingly enough after calling this method "authorization" header always changes to "Authorization". Then I found the thing that particularly interesting for us:
Note that, in keeping with the HTTP RFC, HTTP header field names are case-insensitive.
Keep in mind that I even tried to change URLRequest's allHTTPHeaderFields directly. Had the same result.
Which leads us to the following conclusion: Apple intentionally ignores input headers' case and very irresponsibly changes it (again intentionally since it takes at least a few lines of code somewhere instead of just plugging given headers directly into request). As of now I know no possible solution to this problem (if we want to classify it as a problem which is a bit controversial). Search says that is exists from earlier days of iOS (http://0xced.blogspot.com.by/2010/06/fixing-nsmutableurlrequest.html). You could call some private objective-c APIs which could help, but in fact you'll get unstable or undefined behavior and would likely get rejected from App Store.
So my conclusion, and probably the only right choice in this situation is to change your API.
Configuration is optional, the only thing you need is to setup request right. Make sure (double sure) that you format your auth correctly.
In some (not that rare cases this should be formatted like this:
["Authorization": "Bearer <#your_token#>"]
And what I found about Slim 2 it's also with Bearer so maybe you missing this.
https://github.com/dyorg/slim-token-authentication/tree/master/example#making-authentication-via-header
Example from this:
$ curl -i http://localhost/slim-token-authentication/example/restrict -H "Authorization: Bearer usertokensecret"
With this, you can also check if this working with simple curl command. It should. If not, there is definitely a problem with fields you're sending rather than Alamofire itself.
In docs for Alamofire you can find:
/// Creates a `DataRequest` using the default `SessionManager` to retrieve the contents of the specified `url`,
/// `method`, `parameters`, `encoding` and `headers`.
///
/// - parameter url: The URL.
/// - parameter method: The HTTP method. `.get` by default.
/// - parameter parameters: The parameters. `nil` by default.
/// - parameter encoding: The parameter encoding. `URLEncoding.default` by default.
/// - parameter headers: The HTTP headers. `nil` by default.
///
/// - returns: The created `DataRequest`.
public func request(_ url: URLConvertible, method: Alamofire.HTTPMethod = default, parameters: Parameters? = default, encoding: ParameterEncoding = default, headers: HTTPHeaders? = default) -> Alamofire.DataRequest
Here is an example:
Alamofire.request("https://...",
method: .get,
parameters: ["myKey1": "myValue1"],
encoding: JSONEncoding.default,
headers: self.authHeader).responseJSON { response in
//your response
}
I am a newbie to iOS and using Alamofire in my app. Everything is working fine. Now I want to implement network errors. I have searched about it and here are my findings:
We can implment request timed out in the following way:
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 10
let urlVerifyEmail = ####
manager.request(urlVerifyEmail, method: .post, parameters: ["user_email" : email], encoding: JSONEncoding.default, headers: nil).responseJSON(completionHandler: { response in
switch response.result {
case .success:
print (“success”)
case .failure(let serverError):
if (serverError._code == NSURLErrorTimedOut)
{
print(”Request timed out”)
}
else
{
print(”Error sending request to server”)
}
}
}
)
I have read there official docs too. However I am not having much clarity.
What does request timed out actually mean? Does it mean that the app is taking too long to send the request? or server is taking too long to send the response back?
What are other types or network errors in Alamofire? What if the connection wasn't successfully made? What if it broke while the request was getting sent? What if it broke while the response was coming back?
If error code for request timed out is -1001 then what are the codes for other network errors? In order to provide the users with the best experience, which is the best approach to cover all of network errors?
I'm new to Alamofire so I'm sorry if this it's a noob question: this framework stores the cookies automatically?
This is because I have a simple request like this:
Alamofire.request(.POST, loginURL, parameters: ["fb_id": fbId, "fb_access_token": fbToken])
.responseJSON { response in
//print(response.request) // original URL request
//print(response.response) // URL response
//print(response.data) // server data
//print(response.result) // result of response serialization
if let JSON = response.result.value {
print("loginURL - JSON: \(JSON)")
}
}
this request response with a cookie session that I need to do other requests for security reason; the strange thing is that like magic I already can do the other requests after this first POST without read manually the cookie and store it. I'm sure the other requests need the cookie session because they fail on postman for example but not here.
It's just a feature? Because I can't find anything on that also on the official GitHub page.
Yes! Alamofire is basically a wrapper around NSURLSession. Its manager uses a default NSURLSessionConfiguration by calling defaultSessionConfiguration().
As its github page says under Advanced Usage section:
Alamofire is built on NSURLSession and the Foundation URL Loading System. To make the most of this framework, it is recommended that you be familiar with the concepts and capabilities of the underlying networking stack.
And under Manager section:
Top-level convenience methods like Alamofire.request use a shared instance of Alamofire.Manager, which is configured with the default NSURLSessionConfiguration.
And the NSURLSessionConfiguration reference for defaultSessionConfiguration() says:
The default session configuration uses a persistent disk-based cache (except when the result is downloaded to a file) and stores credentials in the user’s keychain. It also stores cookies (by default) in the same shared cookie store as the NSURLConnection and NSURLDownload classes.
For those who use Moya and want to disable stored cookies
(fixing the X-CSRF-Token request header is missing)
Very basic example:
public final class DisableCookiePlugin: PluginType {
public init() {
}
public func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
var mutableRequest = request
mutableRequest.httpShouldHandleCookies = false
return mutableRequest
}
}
And then use it
MoyaProvider<Api>(
plugins: [
//NetworkLoggerPlugin(configuration: .init(logOptions: .verbose)),
DisableCookiePlugin()
]
So I'm sending a basic auth request to Bing Image Search to grab some image data, and it was working great, right until I updated to the latest version of Alamofire (1.3 -> 2.0.2), which I had to do because 1.3 wasn't even close to compatible with XCode 7.
Anyway, here is my code:
let credentials = ":\(Settings.bingApiKey)"
let plainText = credentials.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
let base64 = plainText!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
manager = Alamofire.Manager.sharedInstance
manager!.session.configuration.HTTPAdditionalHeaders = [
"Authorization": "Basic \(base64)"
]
let url = NSURL(string: Settings.bingImageApi + "&Query=" + keyword + "&$top=15&$skip=" + String(skip))!
manager!
.request(.POST, url, parameters: nil, encoding: .JSON)
.responseJSON { request, response, result in
...
And I'm getting the error:
FAILURE: Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.}
The authorization type you provided is not supported. Only Basic and OAuth are supported
I had the same issue while moving from Alamofire 1.x to 2.x.
One workaround I found (and that works), is to pass the headers when performing the request:
let headers = ["Authorization": "Basic \(base64)"]
Alamofire.request(.POST, url, parameters: nil, encoding: .JSON, headers: headers)
For more information you can take a look at the documentation.
please read here http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/
"App Transport Security (ATS) lets an app add a declaration to its Info.plist file that specifies the domains with which it needs secure communication. ATS prevents accidental disclosure, provides secure default behavior, and is easy to adopt. You should adopt ATS as soon as possible, regardless of whether you’re creating a new app or updating an existing one."
The first part of the error is due to you not receiving valid JSON in the response. You can use response, responseData or responseString to help debug.
The second part of the error is due to how you are setting the header. You cannot set an Authorization header after the session configuration has been created. You can either create your own session configuration and your own Manager, or you can pass the Authorization header in the request.