AFNetworking setAuthorizationHeaderWithUsername:password: vs. setDefaultCredential - ios

I'm using AFNetworking and associated goodies, and I've subclassed AFOAuth2Client to do some OAuth work with tokens and so forth.
For testing purposes, I'm working on a dev server that requires a username & password to access the pages/API endpoints. When I browse to one of the pages, I get a little dialog box, enter the credentials, and I'm good for some time. As I understand it this is "Basic Authentication" or "Basic Access Authentication".
So when I instantiate my AFOAuth2Client subclass, I use
[self setAuthorizationHeaderWithUsername:#"user" password:#"pass"];
with the appropriate username and password that I would type into my browser when prompted, but when I attempt to access the dev server, using the HTTPOperationWithRequest: method, I get rejected.
So instead, when I instantiate the client, I try:
NSURLCredential *credential = [NSURLCredential credentialWithUser:#"user" password:#"pass" persistence:NSURLCredentialPersistenceNone];
[self setDefaultCredential:credential];
And this works fine for all requests I make; the server lets me in.
Now as far as I understand, the first method is setting the Authorization: HTTP header with my credentials, and the second method tells my request that when it gets challenged, set the Authorization: HTTP header with my credentials...same thing, no?
Why would the first method fail and the second one succeed?

Related

OAuth2.0 works from Postman UI (authorization helper), doesn’t work via manual request

The issue I’m facing is I’m trying to manually get the token from the API for the sake of automation. What I did is I configured IdentitySever to grant token on user credentials it worked fine when request has been sent via OAuth UI:
I’m getting the following request/response (viewed in console):
Now the issue is that I’m sending the exact same request but manually, but it fails:
I'm getting 'invalid_client' error instead of Token
I'm not overriding IResourceOwnerPasswordValidator so I'm using default implementation.
Anything else that I’m missing? Does UI do something else under the hood? Did I miss something?
The requests are identical, I copied over clients, passwords etc in case anything is different.
I’ve also tried to do the token request via get+query string, but same result
I tried changing the clientid, clientsecret, but no luck so far
There's a typo in your manual request. It should be 'client_secret' but not 'cliend_secret'.
OAuth2 doesn't understand this field and as a result of this, it assumes that you didn't pass the secret of the client in the request and thus it throws an 'invalid_client' error.

How to use Omniauth Asana with Rails API only app

I have a Rails 5 API only app and an Angular JS Frontend app and would like to integrate with Asana API. I'm using the ruby-asana, omniauth and omniauth-asana gems.
I start the request using Asana's JS library like so:
var client = Asana.Client.create({
clientId: 172706773623703,
clientSecret: '<client_secret>',
redirectUri: '<redirect_url>'
});
client.useOauth({
flowType: Asana.auth.PopFlow
});
And the above does redirect me to Asana where I can login. On the redirectUri I'm giving a backend route (Rails 5 API only) which should handle the remaining on the authentication (using the JS only I get only a temporary token that cannot be self renewed meaning the user will have to authenticate every time the token expires. This is if I understood the documentation correctly).
So, on the controller I've created to handle the route, I have the following (from an example on Asana's documentation):
require 'omniauth-asana'
use OmniAuth::Strategies::Asana, <secret>, <secret>
creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') }
strategy = request.env["omniauth.strategy"]
access_token = OAuth2::AccessToken.from_hash(strategy.client, creds).refresh!
$client = Asana::Client.new do |c|
c.authentication :oauth2, access_token
end
Now, the above doesn't work because 1) there's no request.env as this is an API only app, so I've followed the instruction on Omniauth and have added the following to my config/application.rb:
config.session_store :cookie_store, key: '_interslice_session'
config.middleware.use ActionDispatch::Cookies # Required for all session management
config.middleware.use ActionDispatch::Session::CookieStore, config.session_options
Now, in the request.headers I have _interslice_session which has some numbers. How can I create a Asana client with the above?
Any ideas?
OK, I think I see what you're attempting to do here; I think the best way forward is to start with how OAuth's Authorization Code Grant happens in general, then move into specifics for OmniAuth.
You send the user to a URL that Asana owns; that is, your goal is to get the user to visit a particular url. For Asana, this is https://app.asana.com/-/oauth_authorize. (Note that we respond with an error if you don't sent a correct client_id param, but feel free to check that link if you want). Do not send the client_secret during this request - it is intended to never be involved in client-side code, as this is insecure.
If they agree to give access, Asana sends back a redirect request to the user's browser with a short-lived code. That then means that your server will be called from the user's browser with this code as a parameter, so has to handle a new incoming request from the browser to whatever you specified as your redirect URI. Also, this location must be accessible by all users of your integration wherever they are.
You send this code from your server as a POST request to https://app.asana.com/-/oauth_token with your client_secret to Asana for a refresh token. This is where your application actually asks for credentials; the token given in the previous phases simply acknowledges that for a short time, the user has given your app permission to ask for these credentials, and your client_secret assures Asana that, for this server-side request, your app really is yours (it's like your application's password).
We send back an access_token which represents (approximately) a client-user credential pair that is valid for an hour.
You use these credentials to access our API on behalf of this user. We also send back a refresh_token which is long-lived, and used to get new short-lived access_tokens after they expire in a very similar way.
OK, so how this works with OmniAuth if I grok it correctly is that it expects to handle almost all of it. I'll be working through our omniauth example in our ruby-asana client library here: https://github.com/Asana/ruby-asana/blob/master/examples/omniauth_integration.rb
You set up OmniAuth with your client id and client secret
use OmniAuth::Strategies::Asana, <client_id>, <client_secret>
A request comes in, and you don't have credentials for it.
get '/' do
if $client
...
else
'sign in to asana'
end
end
The user clicks the sign in link, which (code omitted) sends them to the sign_in endpoint. This endpoint issues a redirect to /auth/asana
The browser requests /auth/asana from our server. If you look at that example, it's not implemented in our code. That's because the /auth/:provider is magically handled by OmniAuth.
This is where all the magic happens. OmniAuth handles the entire login flow above: send browser to our oauth_authorize above, then receive the callback and sticks the relevant params in the environment such that it knows "we just got the short lived code". By the time these lines get hit:
creds = request.env["omniauth.auth"]["credentials"].tap { |h| h.delete('expires') }
strategy = request.env["omniauth.strategy"]
you are inside a callback that OmniAuth has intercepted, gotten the needed creds, and set the creds in the environment. You shouldn't have to handle the oauth callback and token exchange manually.
Now, with the code you provided, I notice a few things right off:
You are causing the popup cycle to happen client side. It may be (and I strongly suspect) that this won't work with OmniAuth - it expects to handle the whole OAuth flow.
Based on the code snippet you provided, you aren't serving this out of a request-response cycle in the controller, rather, it appears that this is in the controller body and not out of an instance method. It may be a typo, but this needs to be in a method that is called back to outside of Rails (that is, a route must point to this a controller method that Asana can use to handle the browser request).
You shouldn't have to look at request.headers, I think - I'm not sure what the issues might be with request.env, but I suspect they may be unrelated to the API-only nature of your app. Are you sure that this is because it's API-only? After adding in the middleware, did you double-check that you can't access request.env? My hunch would be that persistent data in request.env will still be there, only it would require on the middleware being added in to do this. The instructions on OmniAuth simply say that you need to have a session store for your API - which makes sense to me, because APIs don't necessarily need to store state across requests, OmniAuth is telling you to put a session store back in.
I know this is a lot of info, but hopefully it helps you get on the right track. Cheers!

How to request access token from Battle.net OAuth with authorization code?

I have a hobby project in mind to use battle.net login. I'm wondering how I can obtain the access token from the API after receiving the authorization code.
This is Oauth flow question rather than a battle.net question.
Currently I can successfully authorize the user for my app which is registered in dev.battle.net and then I try to use the authorization code returned from the battle.net login to obtain the access token by sending a request to https://<region>.battle.net/oauth/token.
However I keep receiving this error:
{
"error": "unauthorized",
"error_description": "An Authentication object was not found in the SecurityContext"
}
I use postman extension to send post requests to that uri. I authenticate my request with my client id and secret. I pass redirect_uri (https://localhost), granty_type (authorization_code), code(the code returned from the previous authorization step). However I keep getting the error above.
I couldn't find much about battle.net online. There are other oauth related help articles but couldn't really find my way.
Wondering if you can help me with this easy stuff. I'm just wondering what I'm skipping here.
Here is the documentation:
https://dev.battle.net/docs/read/oauth
https://localhost is added in my mashery dev account's app settings.
Me again, I resolved this problem after trying almost every combination in the universe:)
Steps to apply:
Don't use the same authorization token for different access token trials, they are not valid
Always use https on every domain you test including localhost, you
redirect_uri must be https as well.
You must use the "basic authentication" in the header of your POST request while requesting the token from the authorization code you obtained from the previous step.
This is one of the most important ones: For requesting token, Pass redirect_uri, client key and secret as POST form parameters to the authenticated request. This is interesting because it's already an authenticated request; why would i need to pass my secret again? Anyways, that's how it works.
Here are the full text:
http://hakanu.net/oauth/2017/01/26/complete-guide-of-battle-net-oauth-api-and-login-button/
This is working prototype:
https://owmatch.me
Thanks.

HTTPS + Form Logon NSURLConnection

I am using HTTPS to a form-logon page.
When intercepting via
- (void)connection:(NSURLConnection *)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge`
and extracting the Authentication Method used via
NSString *authenticationMethod = [[challenge protectionSpace] authenticationMethod];
I get the following
NSURLAuthenticationMethodServerTrust
But expected result should be
NSURLAuthenticationMethodHTMLForm
Is this due to using HTTPS?
Short answer: Yes
The purpose of the NSURLAuthenticationMethodServerTrust authentication method is that the client can verify and trust that the server is actual the server it pretends to be.
The NSURLAuthenticationMethodHTMLFormis used to authenticate a user via a web form. The server sends a web form and requests user credentials. This authentication does not require to be send over SSL/TLS. But then the user's credentials will be send in the clear, which is a bad thing from a security point of view.
Client authentication is also part of the TLS protocol. In this case, you may receive a challenge whose method is NSURLAuthenticationMethodClientCertificate.
Notice, you may receive more than one authentication challenges.

Revoking OAuth Access Token Results in 404 Not Found

I'm working on an application that integrates with GitHub and am having issues "logging out" a user that was previously authenticated. When I attempt to revoke the authorization token for the user, I get a 404 Not Found response from the API.
According to the documentation, it looks like I should just be able to make a DELETE request to https://api.github.com/authorizations/[authTokenId]. I have tried a couple of different things including:
Ensuring the Authorization header is set with the current auth token
Ensuring the UserAgent header is set with what I use for the rest of the API calls
Nothing seems to result in anything but a 404 though. I have validated that the token is valid and has that the Id matches with what is expected (id property from the authorization response and from the "check an authorization" response as well). Anyone have another thought on something I could be missing?
Looks like currently you need to include a basic authentication header (including a base64 encoded string of your username/password).
Not ideal for my purposes since I want to revoke the token when a user "logs out" of my application and I don't want to store their username/password. I've sent GitHub support an email about it to see if they have any other ideas.
Update 6/12/2013
GitHub support has stated that the above is expected at this juncture, but they are considering updating to allow revoking an authorization using the authorization as the means of authentication.
For now I'm going to require the user to enter their username/password a second time to revoke the authorization.

Resources