Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic Auth user credentials not being passed with request #32

Closed
mikecole20 opened this issue Aug 11, 2014 · 19 comments
Closed

Basic Auth user credentials not being passed with request #32

mikecole20 opened this issue Aug 11, 2014 · 19 comments

Comments

@mikecole20
Copy link

I'm trying to use the example from NSHipster to do Basic Auth. I form my request like so:

        Alamofire.request(.POST, "https://my.url.com/api/v1/auth")
            .authenticate(HTTPBasic: email, password: password)
            .responseJSON {(request, response, JSON, error) in
                println(request)
                println(response)
                println(JSON)
                println(error)
        }

Email and password are just strings here. I am getting a response suggesting that the credentials aren't attached.

Optional(<NSHTTPURLResponse: 0x7fb699783080> { URL: https://my.url.com/api/v1/auth } { status code: 401, headers {
    Allow = "POST, OPTIONS";
    Connection = "keep-alive";
    "Content-Language" = "en-us";
    "Content-Length" = 59;
    "Content-Type" = "application/json";
    Date = "Mon, 11 Aug 2014 13:34:36 GMT";
    Server = "gunicorn/17.5";
    Vary = "Accept, Accept-Language";
    "Www-Authenticate" = "Basic realm=\"api\"";
    "X-Frame-Options" = SAMEORIGIN;
} })
Optional({
    detail = "Authentication credentials were not provided.";
})

When I make the same request while using AFNetworking, I get a successful 200 response.

        let manager = AFHTTPRequestOperationManager()
        manager.requestSerializer.clearAuthorizationHeader()
        manager.requestSerializer.setAuthorizationHeaderFieldWithUsername(emailTextField.text, password:passwordTextField.text)
        manager.POST(
            "https://my.url.com/api/v1/auth",
            parameters: nil,
            success: { (operation: AFHTTPRequestOperation!,
                responseObject: AnyObject!) in
                println("JSON: " + responseObject.description)
            },
            failure: { (operation: AFHTTPRequestOperation!,
                error: NSError!) in
                println("Error: " + error.localizedDescription)
        })

If I'm making a mistake in my request with AlamoFire, would you mind pointing it out?

@rjsamson
Copy link

I'm having the same issue. The example in the README actually doesn't work either.

@mattt
Copy link
Sponsor Contributor

mattt commented Aug 14, 2014

These are doing different things. Alamofire is creating a credential that will be used during an authentication challenge, whereas AFNetworking is setting the Authorization header in the initial request.

@luketheobscure
Copy link

Came here to ask this exact question. @mattt I understand your answer, but maybe update the ReadMe to explain a bit more?

@mikecole20
Copy link
Author

Thanks for the clarification @mattt.

I'll explain for anyone else who may be running into this issue. Based on the implementation in AFNetworking, what I need to do is create the Authorization header myself instead of using the current authenticate method. In AFNetworking (and in general) this header should look like this.

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

Here, QWxhZGRpbjpvcGVuIHNlc2FtZQ== is username:password after Base64 encoding.

The issue with using AlamoFire over AFNetworking (for now) is that you have to do the Base64 encoding yourself. AFNetworing included an encoding implementation which was called behind the scenes while creating the header. All you had to do is pass in the username and password. That being said, I imagine @mattt or someone will add that functionality into AlamoFire at some point.

@zkirill
Copy link

zkirill commented Sep 2, 2014

This caught me too. Thank you for explaining! Code below to hopefully save someone else time.

let plainString = "username:password" as NSString
let plainData = plainString.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.fromRaw(0)!)
Alamofire.Manager.sharedInstance.defaultHeaders["Authorization"] = "Basic " + base64String!

Encoding code adopted from http://ios-blog.co.uk/tutorials/quick-tips/base64-decoding-in-ios-7-objective-c-and-ios8-swift/

@macu
Copy link

macu commented Sep 29, 2014

How about adding a pre-auth param to the authenticate method, with default value false?

For comparison, HttpWebRequest in .NET uses a PreAuthenticate property. I'm porting apps from Xamarin/C# to Swift.

@loopj
Copy link

loopj commented Oct 5, 2014

This caught me too.

Many APIs (including GitHub's popular API) do not send the www-authenticate header, and just expect an Authorization header to be sent with any requests, so the current behavior is not ideal.

@loopj
Copy link

loopj commented Oct 5, 2014

For future travelers, if you'd like to pre-authorize:

let plainString = "\(user):\(password)" as NSString
let plainData = plainString.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.fromRaw(0)!)

Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders = ["Authorization": "Basic " + base64String!]

Alamofire.request(.GET, "http://example.com")
    .response {(request, response, _, error) in
        println(response)
    }

@brow
Copy link
Contributor

brow commented Feb 9, 2015

Update for people seeing @loopj's comment in 2015: the README now notes that

[Modifying the session configuration] is not recommended for Authorization or Content-Type headers. Instead, use URLRequestConvertible and ParameterEncoding, respectively.

@zkirill
Copy link

zkirill commented Feb 9, 2015

@brow Thanks for the heads up. Can someone please comment on why this is not recommended?

@brow
Copy link
Contributor

brow commented Feb 9, 2015

I think the idea is that NSURLConnection is designed to manage stateful authorization for you according to RFC 2617 and therefore doesn't expect you to override the Authorization header.

@eraye1
Copy link

eraye1 commented Mar 31, 2015

Just got trolled by this... 👊

@skywinder
Copy link
Contributor

Same thing. 👊 :
Quite confusing after AFNetworking (since I worried, when found, that authenticate called after request), but it's quite unclear for first time usage.

And update for @loopj code in favor of Swift 1.2:

let plainString = "\(user):\(password)" as NSString
let plainData = plainString.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData?.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))

Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders = ["Authorization": "Basic " + base64String!]

@cslosiu
Copy link

cslosiu commented May 26, 2015

Very helpful post. Save my day!

@tomj
Copy link

tomj commented May 30, 2015

Thanks @skywinder - @mattt perhaps we could simply point out in the README that the HTTP basic auth challenge in Alamofire is different in nature to setAuthorizationHeaderFieldWithUsername in AFNetworking?

@skywinder
Copy link
Contributor

@mattt I create feature-request #502, that can also solve this confusion in auth behaviours.

@blevine
Copy link

blevine commented Jun 19, 2015

Using the Alamofire.Manager.sharedInstance seems a little dangerous if, for example, I'm making multiple requests to different endpoints that require different credentials. Doing preemptive authentication seems like it's common enough to warrant an enhancement to the API. I like @macu's suggestion of adding a PreAuthenticate property or something similar

@joshuaziering
Copy link

Just ran into this as well. +1 for Authorization headers....

@raeidsaqur
Copy link

As an update to @loopj @skywinder implementation, with AlamoFire > 4.0.0, you can alternatively use the static func authorizationHeader in Request.swift, which conveniently does the encoding for you. Something like:

static func getRequestHttpHeaders() -> HTTPHeaders {
    let authTuple: (key: String, value: String)? = Request.authorizationHeader(user: user, password: password)
    return [authTuple?.key : authTuple?.value, $(OTHER_HEADERS)]
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests