renesteg
renesteg

Reputation: 571

Invalid grant_type parameter or parameter missing on POST for requesting access token

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

Answers (4)

user14719581
user14719581

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

Francois Bourgeois
Francois Bourgeois

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:

  • get an authorization code (the user authenticates here outside of your app before the flow passes back to you via redirect)
  • change the authorization code into an access token

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

Abhishek Tyagi
Abhishek Tyagi

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

Eugenio Pace
Eugenio Pace

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

Related Questions