Reputation: 571
I am struggling to get the access token on Quizlet (oauth2). Everything works fine so far, I can make the user accepting my app on Quizlet, get redirected, but when requesting the access token via NSURLConnection, I always get the following error:
2013-08-17 09:39:33.422 Abiliator[49549:c07] Returned data in string format: {"http_code":400,"error":"invalid_request","error_title":"Not Allowed","error_description":"Invalid grant_type parameter or parameter missing"}
Here the code for the user authentication (must be via browser according to spec):
- (void) authenticateQuizletUser
{
NSString *quizletRandomString = [abiliatorAppDelegate GetUUID];
NSString *authURLString = [@"https://quizlet.com/authorize/?response_type=code&client_id=" stringByAppendingString:@"<myID>&scope=read"];
authURLString = [authURLString stringByAppendingString:@"&state="];
authURLString = [authURLString stringByAppendingString:quizletRandomString];
authURLString = [authURLString stringByAppendingString:@"&redirect_uri=Abiliator://after_oauth"];
NSLog(@"Authentication URL sent: %@", authURLString);
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: authURLString]];
}
That works fine, as I mentioned. The app starts Safari and the user must acknowledge the request entering user id and password and the server redirects into my app, which I catch in the method below, which then throws the error described.
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
if (!url) { return NO; }
NSString *URLString = [url absoluteString];
NSLog(@"Received URL: %@", URLString);
NSString *myURLQuery = [url query];
NSString *myAuthCode = [self getAuthorizationCodeFromURL:myURLQuery];
NSLog(@"Component1: %@", myAuthCode);
NSString *authPasswd = @"myPasswd";
NSString *username=@"myUserName";
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://api.quizlet.com/oauth/token"]];
request.HTTPMethod = @"POST";
[request setValue:@"application/x-www-form-urlencoded; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"Abiliator://after_oauth" forHTTPHeaderField:@"redirect_uri"];
// According to Quizlet API doc: You must set this (grant_type) to the string "authorization_code".
[request setValue:@"authorization_code" forHTTPHeaderField:@"grant_type"];
[request setValue:myAuthCode forHTTPHeaderField:@"code"];
NSString *authStr = [NSString stringWithFormat:@"%@:%@", username, authPasswd];
NSData *authData = [authStr dataUsingEncoding:NSASCIIStringEncoding];
NSString *authValue = [NSString stringWithFormat:@"Basic %@", [authData base64EncodedString]];
[request setValue:authValue forHTTPHeaderField:@"Authorization"];
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
return YES; }
Any help hihgly appreciated.
Upvotes: 6
Views: 12457
Reputation: 31
I also struggled with similar issue, here is what worked for me.
Need to define Header right (if I explain it Karate script way),
* def keycloak_extension = "keycloak authorization to generate the access token put here"
* configure headers = {'Content-Type':"application/x-www-form-urlencoded",'Authorization':#("Basic " +keycloakAuthSecret)}
Given path keycloak_extension
And form field grant_type = 'client_credentials'
And request '{}'
When method post
And status 200
* def accessToken = response.access_token ```
How to encode using Base64 can use this link:
https://www.base64decode.org/
Can also refer to this video, to understand it better:
https://www.youtube.com/watch?v=k-Q-Kywk1O4
Upvotes: 0
Reputation: 3690
OAuth2 defines 4 ways to gain an access token. The most secure and most complicated is the Authorization Code Grant that is used by Quizlet as described here.
The Authorization Code Grant consists of two steps:
You did the first call right. The problem with the second call is that you put the grant_type
parameter in the wrong place of your request.
In this line you treat it as an HTTP header:
[request setValue:@"authorization_code" forHTTPHeaderField:@"grant_type"];
And here you also treat the authorization code as an HTTP header:
[request setValue:myAuthCode forHTTPHeaderField:@"code"];
But OAuth2 requires you to put both into the body of your request. Here is an example of a correct request:
POST /oauth/token/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Content-Length: 999
Authorization: Basic xxx
grant_type=authorization_code&code=theCodeYouGotInTheFirstStep&
scope=somescope&redirect_uri=theSameUriYouIncludedEarlier
(The stuff below the empty line is your request's body)
(I added the linebreak in the body only for readability - you must not include it in a request)
Bonus answer: Please keep in mind that OAuth2 is insecure by default: If you don't do a bit of extra work, your app is vulnerable to Cross-Site Request Forgery attacks as even mentioned in the OAuth2 RFC. To prevent this OAuth2 offers you the state
parameter. You have to generate a non-guessable value for your state
and include it in the first request. You must not fire the second request, if the state
returned by the server is not the same you generated earlier.
Upvotes: 2
Reputation: 2249
Send your oauth parameters as payload. You should not be sending them as headers. For Example your request should looks like
POST /token
Headers:
Authorization: Basic R0cxSWJdHpINDVhang5Y0VlSUxxalBSOW5SU0NBWA==
Content-Type: application/x-www-form-urlencoded
Payload:
grant_type=authorization_code&code=Wirr951e&scope=READ&redirect_uri=www.google.com
Upvotes: 0
Reputation: 14212
Might be useful to see the rest of your implementation. How are you obtaining the code
? Also, what credentials (authPasswd
& username
) are you using? These should be your app's (as opposed to the end user's). You get these in Quizlet's Dev Dashboard.
Note: this OAuth flow is not recommended for devices, as it requires secrets to be stored in the device. Using the
implicit
flow is, but I'm not sure Quizlet supports it.
Upvotes: 1