0

Can anyone help me understand OAuth flow for Khan Academy API. It can be accessed through this link: https://github.com/Khan/khan-api/wiki/Khan-Academy-API-Authentication

I am using Scribe.

Here is the working code:

     OAuthService serv = new ServiceBuilder().provider(KhanApi.class)
     .apiKey("").apiSecret("")
     .build();
     Token token = new Token("", "");

     OAuthRequest req = new OAuthRequest(Verb.GET,
     "http://www.khanacademy.org/api/auth/request_token");

     serv.signRequest(token, req);

     Response resp = req.send();
     System.out.println(resp.getBody());

After you get the response you need to redirect user to that page. Then after a successful login browser will respond with a url which contains request token...

Here are two different things that I tried and didn't work out well and their results:

1)

 OAuthService serv = new ServiceBuilder().provider(KhanApi.class)
 .apiKey("***").apiSecret("***")
 .build();
 Token token = serv.getRequestToken();

the result is :

Exception in thread "main" org.scribe.exceptions.OAuthException: Response body is incorrect. Can't extract token and secret from this:

<!DOCTYPE html>
<html>
    <head>
        <title>Login to Khan Academy</title>

        <style>
            #login-page {
                padding-top: 18px;
            }
            .providers {
                height: 100px;
            }
            .providers .provider .img-container {
                height: 80px;
            }

           .horizontal-separator .separator-text {
                background-color: white;
                margin-left: 185px;
            }
            .pw-login {
                width: 415px;
                height: auto;
                text-align: right;
            }
            .pw-login img.tree {
                float: left;
            }

        </style>


    </head>
    <body>
        <article id="login-page">
            <div id="login-inner-content">
                <form method="POST"
                    class="auth-form"
                    id="login-form"
                    action="https://khan-academy.appspot.com/login/mobileoauth">
                <h3>Login to Khan Academy</h3>
                <input type="hidden" name="oauth_map_id" value="889298340">
                <input type="hidden" name="view" value="normal">


                <ul class="providers">
                    <li class="provider action-gradient" title="Google">

            </div>
        </article>
    </body>
</html>'
    at org.scribe.extractors.TokenExtractorImpl.extract(TokenExtractorImpl.java:41)
    at org.scribe.extractors.TokenExtractorImpl.extract(TokenExtractorImpl.java:27)
    at org.scribe.oauth.OAuth10aServiceImpl.getRequestToken(OAuth10aServiceImpl.java:52)
    at com.saeid.scribe.oauth.Main.main(Main.java:117)

The response is a string of a broken html file.(broken in the sense that images are not being shown...)

2) Also I tried:

    OAuthRequest req = new OAuthRequest(Verb.GET,
            "http://www.khanacademy.org/api/auth/request_token");

    Map<String, String> parameters = generateParameters("GET",
            "http://www.khanacademy.org/api/auth/request_token");

    req.addQuerystringParameter("oauth_consumer_key", parameters.get("oauth_consumer_key"));
    req.addQuerystringParameter("oauth_nonce", parameters.get("oauth_nonce"));
    req.addQuerystringParameter("oauth_signature", parameters.get("oauth_signature"));
    req.addQuerystringParameter("oauth_signature_method", parameters.get("oauth_signature_method"));
    req.addQuerystringParameter("oauth_timestamp", parameters.get("oauth_timestamp"));
    req.addQuerystringParameter("oauth_version", parameters.get("oauth_version"));

    Response res = req.send();

the result is:

OAuth error. Invalid signature. Expected signature base string: GET&http%3A%2F%2Fwww.khanacademy.org%2Fapi%2Fauth%2Frequest_token&oauth_consumer_key%3D***********%26oauth_nonce%3D1341526030%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1341526030%26oauth_version%3D1.0

I am using the same base string to generate signature and I am using consumer key as the key for HMAC-SHA1 method.

Here is the how KhanAPI looks like: import org.scribe.builder.api.DefaultApi10a; import org.scribe.model.Token;

public class KhanApi extends DefaultApi10a{

@Override
public String getAccessTokenEndpoint() {
    return "http://www.khanacademy.org/api/auth/access_token";
}

@Override
public String getAuthorizationUrl(Token arg0) {
    return "";
}

@Override
public String getRequestTokenEndpoint() {
    return "http://www.khanacademy.org/api/auth/request_token";
}

}

Can anyone help me? Thanks,

Jeff
  • 12,147
  • 10
  • 51
  • 87
Saeid Farivar
  • 1,667
  • 24
  • 43

2 Answers2

1

doesn't Scribe add all of the required OAuth parameters, and perform the signing for you? i'm looking at the LinkedIn example, which is based off of a default OAuth 1.0a flow, and i don't see it doing anything like that.

https://github.com/fernandezpablo85/scribe-java/blob/master/src/test/java/org/scribe/examples/LinkedInExample.java

if you have to manually formulate the URL and sign it yourself, what exactly is Scribe doing for you?

Jeffrey Blattman
  • 22,176
  • 9
  • 79
  • 134
  • Thanks for the reply. yes you're right, I also tried scribe. the problem is the API provider didn't provide any authenticationURL which is needed to implement the API class. Can you please take a look at the oath flow of their API and let me know what is your understanding? – Saeid Farivar Jul 05 '12 at 22:35
  • are you saying that your API provider only support 2-legged oauth? if so, i do not think Scribe supports that (easily). you are experiencing first hand why OAuth 2.0 does not require signing ... it's a pain to get right. – Jeffrey Blattman Jul 05 '12 at 22:37
  • I guess it should be a 2-legged, I am not sure though I am just a beginner. Here is the flow: you need to redirect user to a URL with the parameters such as consumer_key, oauth_signatuer,... then it should redirect user to a webpage which then the user can enter his/her confidential. then the browser will return a url with request token and secret..... is it something that I use Scribe for that? – Saeid Farivar Jul 06 '12 at 01:22
  • are you using Scribe, or not? you seem to be using some classes from some OAuth API, but you are also saying you are signing it manually. either use Scribe and let it add the params for you, or build and send the request manually (using apache http client for example). it's likely that whatever is being sent is actually a mix of Scribe-added params and your own, which would definitely result in an incorrect signing. – Jeffrey Blattman Jul 06 '12 at 04:08
  • What I described is right from their API documentation for OAuth. Actually I tried to send parameters myself but I failed. right now I am assuming that Scribe is doing that for me although I am not sure what values Scribe is sending and are they different than what this API expects or not .... when I use Scribe I receive a html code which I guess that isnot a right response because I cannot see the images also links don't work. Supposedly after a successful login I should be able to fetch the request token for further processes which I am not receiving any parameters... – Saeid Farivar Jul 06 '12 at 05:24
  • i don't think you understand. you are using Scribe, and Scribe (tries) to add all those parameters for you. you don't need to add them if you use Scribe. either use Scribe and let it add them for you, or don't use Scribe, and add them manually. if you are making a request using the class OAuthRequest, you are using Scribe. you need to use a lower-level HTTP library like apache HTTP client or java.net if you want to add those parameters manually. – Jeffrey Blattman Jul 06 '12 at 16:05
  • Got it. right now my code is like: OAuthService serv = new ServiceBuilder().provider(KhanApi.class) .apiKey("***").apiSecret("***") .build(); So I am not adding any parameters. then I try to get the request token with Token token = serv.getRequestToken(); which throws an exception as I have mentioned in my question. – Saeid Farivar Jul 06 '12 at 17:57
  • can you post your KhanApi.java file? also, have you verified the values for the API key and secret are correct? – Jeffrey Blattman Jul 06 '12 at 18:09
  • again, what's the response code from the failed HTTP request? it's probably in the exception. – Jeffrey Blattman Jul 06 '12 at 18:18
  • I updated the question. That's not an http error. that's an exception thrown by running the getRequestToken(). – Saeid Farivar Jul 06 '12 at 18:22
  • the "broken" HTML should report the HTTP error. you need to see what the server is saying in response to Scribe. – Jeffrey Blattman Jul 06 '12 at 18:28
  • ok, now here is the code I tried now OAuthService serv = new ServiceBuilder().provider(KhanApi.class) .apiKey("***").apiSecret("***") .build(); Token token = new Token("", ""); OAuthRequest req = new OAuthRequest(Verb.GET, "http://www.khanacademy.org/api/auth/request_token"); serv.signRequest(token, req); Response resp = req.send(); System.out.println(resp.getBody()); It seems that the response is the login form itself.... – Saeid Farivar Jul 06 '12 at 18:31
  • It seems that it's a 3legged oath flow... If that's the login form then I should redirect user to that for authentication. then according to the api documentation after user logged in successfully I should be able to get the request token... – Saeid Farivar Jul 06 '12 at 18:36
  • Thanks @jeffrey. I was able to get the request token... I will update the question with working code.... – Saeid Farivar Jul 06 '12 at 18:43
0

This api returns an expected base string, which is great. You can run scribe in debug mode and see what base string you're generating, like this:

 OAuthService serv = new ServiceBuilder().provider(KhanApi.class)
 .apiKey("***").apiSecret("***")
 .debug()
 .build();
Pablo Fernandez
  • 103,170
  • 56
  • 192
  • 232