Getting a OneNote token with Java

2019-09-05 06:29发布

I fail to get a valid OneNote token from Java (in a desktop application).

I think I may not understand the whole process of authorization through an application. But I can't find information that clears this up.

Here is the Java code I use:

HttpsURLConnection refreshTokenConnection;
refreshTokenConnection = (HttpsURLConnection) (new URL(TOKEN_REQUEST_URL)).openConnection();
refreshTokenConnection.setDoOutput(true);
refreshTokenConnection.setRequestMethod("POST");
refreshTokenConnection.setDoInput(true);
refreshTokenConnection.setRequestProperty("Content-Type", TOKEN_REFRESH_CONTENT_TYPE);
refreshTokenConnection.connect();

String redirect = URLEncoder.encode(TOKEN_REFRESH_REDIRECT_URL, "UTF-8");

try (OutputStream refreshTokenRequestStream = refreshTokenConnection.getOutputStream()) {
    String requestBody = MessageFormat.format(TOKEN_REFRESH_REQUEST_BODY,
            Constants.CLIENTID, redirect, ACCESS_SCOPE);
    System.out.println(requestBody);
    refreshTokenRequestStream.write(requestBody.getBytes());
    refreshTokenRequestStream.flush();
} catch (Exception ex) {
    System.err.println("while writing request body");
    ex.printStackTrace();
}

if (refreshTokenConnection.getResponseCode() == 200) {
    return parseRefreshTokenResponse(refreshTokenConnection);
}
else {
    throw new Exception("The attempt to refresh the access token failed");
}

The response I get is an html reponse telling me that "Microsoft account is experiencing technical problems. Please try again later" !

<!-- ServerInfo: BL2IDSLGN1H030 2015.12.15.14.26.46 Live1 ExclusiveNew LocVer:0 -->
<!-- PreprocessInfo: BTSA007:RR1BLDF009,  -- Version: 16,0,26014,0 -->
<html dir="ltr">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <base href="https://login.live.com/pp1600/"/>


                                                                <title>Microsoft account</title>
        <meta name="PageID" content="i4400">
        <meta name="SiteID" content="38936">
        <meta name="ReqLC" content="1033">
        <meta name="LocLC" content=" ">
        <style type="text/css">
            body {
                background-color: #fff;
                color: #000;
                font-family: "Segoe UI","Segoe UI Web Regular","Segoe UI Symbol","Helvetica Neue","BBAlpha Sans","S60 Sans",Arial,sans-serif;
                font-weight: 400;
                font-size: 15px;
                line-height: 20px;
            }
            h1 {
                line-height: 56px;
                font-weight: 200;
                margin: 20px 0;
            }
            h2 {
                font-size: 34px;
                font-weight: 200;
                line-height: 40px;
                padding: 3.18px 0;
            }
            p {
                margin: 12px 0;
                padding: 0.22px 0;
            }
            .header, .content {
                width: 90%;
                margin: 0 auto;
            }
            .logo {
                vertical-align: middle;
            }
            .site-identifier {
                line-height: 23px;
                font-size: 17px;
                margin-left: 15px;
                padding-left: 15px;
                border-left: 1px solid #aaa;
                color: #aaa
            }
            .footer {
                margin-top: 100px;
            }
        </style>
        <script type="text/javascript">
            function EndPPCRL(rs, u)
            {
                if (external)
                {
                    try
                    {
                        if (rs)
                        {
                            external.RequestStatus = rs;
                            external.WebFlowUrl = u;
                            external.NotifyIdentityChanged();
                            external.ReturnToApp();
                        }
                        else
                        {
                            external._cW(); external.BrowseToAuthUI();
                        }
                    }
                    catch (e) { }
                }
            }
            function BodyLoad()
            {
                EndPPCRL(-2147186656);
            }
        </script>
        <script type="text/javascript">
            WizardExternalHelper = function(){ };
            WizardExternalHelper.prototype =
            {
                setProperty: function(key, value)
                {
                    try
                    {
                        window.external.Property(key) = value;
                    }
                    catch (e) { }
                },
                finalNext: function()
                {
                    try
                    {
                        window.external.FinalNext();
                    }
                    catch (e) { }
                }
            };
        </script>
                <script type="text/javascript">
            try
            {
                var channelMessage =
                {
                    type: "event",
                    value:
                    {
                        name: "CloudExperienceHost.done",
                        data: "fail"
                    }
                };
                window.external.notify(JSON.stringify(channelMessage));
            } catch (e) {};
            try
            {
                var externalHelper = new WizardExternalHelper();
                externalHelper.setProperty("ExtendedErrorString", "");
                externalHelper.setProperty("ErrorCode", parseInt(1));
                externalHelper.setProperty("ErrorString", "This service isn't available right now—please try again later.");
                externalHelper.finalNext();
            } catch (e) {};
            function OnBack() {};
            function OnNext() {};
        </script>
    </head>
    <body onLoad="BodyLoad()">
      <div class="header" id="idHeaderTD9">
        <h1>
            <img src="images/ms-logo-v2.jpg" class="logo" alt=" " />
            <span class="site-identifier">Account</span>
        </h1>
      </div>
      <div class="content">
                    <h2>We're unable to complete your request</h2>
            <p>Microsoft account is experiencing technical problems. Please try again later.</p>
              </div>
    </body>
</html>
<!-- _i-Agent:Java/1.8.0_45 -->

Although If I'm using the same url directly in a browser, I'm redirected to a page where I must log-on and the accept the requested rights. From there, I received the tolken I'm looking for.

This is this latter step I'm failing to understand how to programmaticaly achieve : requiring the acceptance from the user.

== EDIT : OkHttp version ==

The code that brings me a step further:

private final String ACCESS_SCOPE = "office.onenote"; 
private final String TOKEN_REQUEST_URL = "https://login.live.com/oauth20_authorize.srf";
private final String TOKEN_REFRESH_REDIRECT_URL = "https://login.live.com/oauth20_desktop.srf";

HttpUrl base = HttpUrl.parse(TOKEN_REQUEST_URL);

HttpUrl hurl;
HttpUrl.Builder urlBuilder = new HttpUrl.Builder();
urlBuilder.scheme(base.scheme());
urlBuilder.port(base.port());
urlBuilder.host(base.host());
urlBuilder.encodedPath(base.encodedPath());
urlBuilder.encodedQuery(base.encodedQuery());

// response_type=token&client_id={0}&redirect_uri={1}&scope={2}
urlBuilder.addQueryParameter("response_type", "token");
urlBuilder.addQueryParameter("client_id", Constants.CLIENTID);
urlBuilder.addQueryParameter("redirect_url", TOKEN_REFRESH_REDIRECT_URL);
urlBuilder.addQueryParameter("scope", ACCESS_SCOPE);
hurl = urlBuilder.build();

Request request = new Request.Builder().url(hurl).build();

Response response = client.newCall(request).execute();

The response's body:

<!DOCTYPE html><!-- ServerInfo: BL2IDSLGN3C026 2015.12.15.14.26.46 Live1 Unknown LocVer:0 -->
<!-- PreprocessInfo: BTSA007:RR1BLDF009,  - Version: 16,0,26014,0 -->
<!-- RequestLCID: 1033, Market:EN-US, PrefCountry: US, LangLCID: 1033, LangISO: EN -->
<html dir="ltr" lang="EN-US"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=Edge"/><base href="https://login.live.com/pp1600/"/><script type="text/javascript">var PROOF = {};PROOF.Type = {SQSA: 6, CSS: 5, DeviceId: 4, Email: 1, AltEmail: 2, SMS: 3, HIP: 8, Birthday: 9, TOTPAuthenticator: 10, RecoveryCode: 11, StrongTicket: 13, TOTPAuthenticatorV2: 14, Voice: -3};</script><noscript><meta http-equiv="Refresh" content="0; URL=https://login.live.com/jsDisabled.srf?mkt=EN-US&lc=1033"/>Microsoft account requires JavaScript to sign in. This web browser either does not support JavaScript, or scripts are being blocked.<br /><br />To find out whether your browser supports JavaScript, or to allow scripts, see the browser's online help.</noscript><title>Sign in to your Microsoft account</title><meta name="PageID" content="i5030"/><meta name="SiteID" content="279469"/><meta name="ReqLC" content="1033"/><meta name="LocLC" content="1033"/><link rel="shortcut icon" href="https://auth.gfx.ms/16.000.26014.00/favicon.ico?v=2" />
        <link rel="stylesheet" title="R3CSS" type="text/css" href="https://auth.gfx.ms/16.000.26014.00/R3WinLive1033.css"/><style type="text/css"></style><style type="text/css">body{display:none;}</style><script type="text/javascript">if (top != self){try{top.location.replace(self.location.href);}catch (e){}}else{document.write(unescape('%3C%73') + 'tyle type="text/css">body{display:block !important;}</style>');}</script><noscript><style type="text/css">body{display:block !important;}</style></noscript><script type="text/javascript">var g_iSRSFailed=0,g_sSRSSuccess="";function SRSRetry(a,f,b){var e=1,d=unescape('%3Cscript type="text/javascript" src="'),c=unescape('"%3E%3C/script%3E');if(g_sSRSSuccess.indexOf(a)!=-1)return;if(typeof window[a]=="undefined"){g_iSRSFailed=1;b<=e&&document.write(d+f+c)}else g_sSRSSuccess+=a+"|"+b+","}
  var g_dtFirstByte=new Date();var g_objPageMode = null;</script><link rel="image_src" href="https://auth.gfx.ms/16.000.26014.00/Windows_Live_v_thumb.jpg" / >
<script type="text/javascript">var ServerData = {aU:'microsoft.com,https://corp.sts.microsoft.com/adfs/ls/?cbcxt=&vv=&username=&mkt=&lc=,urn:federation:MSFT|idcrltestns.com,http://p2.live.com/auth/federation/wsfederator.aspx?Action=login&Domain=idcrltestns.com&cbcxt=&vv=&username=&mkt=&lc=,Test IDCRL Federation Partner',urlSwitch:'https://login.live.com/logout.srf?response_type=token&client_id=000000004017D35F&redirect_url=https://login.live.com/oauth20_desktop.srf&scope=office.onenote&ru=http://oauth.live.net&bk=1451775130&lm=I&pid=15216',aV:"Sign in to .",Bf:1000,AR:false,sCBUpTxt1:'',Bh:'https://auth.gfx.ms/16.000.26014.00/AppCentipede/AppCentipede_Microsoft.png',aX:"Use the primary phone number you\'ve associated with your Microsoft account. <a href=\"http://explore.live.com/windows-live-sign-in-single-use-code-faq\" id=\"idPaneHelpOTCInfoLink9\" target=\"_blank\">Learn more</a>",m:true,AT:0,AU:{},sCBUpTxt2:'',Bi:'',n:false,o:'Pass',AW:'',p:'https://login.live.com/cookiesDisabled.srf?mkt=EN-US&lc=1033',q:true,AX:'https://go.microsoft.com/fwlink/?LinkID=254486',sFedQS:'wa=wsignin1.0&wtrealm=uri:WindowsLiveID&wctx=response_type%3Dtoken%26client_id%3D000000004017D35F%26redirect_url%3Dhttps://login.live.com/oauth20_desktop.srf%26scope%3Doffice.onenote%26bk%3D1451775130',A:true,v:'',B:false,ab:'',urlPost:'https://login.live.com/ppsecure/post.srf?response_type=token&client_id=000000004017D35F&redirect_url=https://login.live.com/oauth20_desktop.srf&scope=office.onenote&bk=1451775130&uaid=4c7c1337346b4a0fb64b97945c57a0f4&pid=15216',C:true,html:[],ad:'',D:1,str:[],a0:'4c7c1337346b4a0fb64b97945c57a0f4',F:'https://account.live.com/ResetPassword.aspx?wreply=https://login.live.com/oauth20_authorize.srf%3fresponse_type%3dtoken%26client_id%3d000000004017D35F%26redirect_url%3dhttps://login.live.com/oauth20_desktop.srf%26scope%3doffice.onenote%26bk%3d1451775130&id=279469&uiflavor=web&client_id=1E00004017D35F&uaid=4c7c1337346b4a0fb64b97945c57a0f4&mkt=EN-US&lc=1033&bk=1451775130',Ab:{'Logo':'','LogoAltText':'','LogoText':'','ShowWLHeader':true},a1:'https://account.live.com/query.aspx?mkt=EN-US&lc=1033',BA:false,sErrTxt:'',ag:true,a2:'',a3:'',H:'https://signup.live.com/signup.aspx?response_type=token&client_id=000000004017D35F&redirect_url=https://login.live.com/oauth20_desktop.srf&scope=office.onenote&bk=1451775130&ru=https://login.live.com/oauth20_authorize.srf%3fresponse_type%3dtoken%26client_id%3d000000004017D35F%26redirect_url%3dhttps://login.live.com/oauth20_desktop.srf%26scope%3doffice.onenote%26mkt%3dEN-US%26lc%3d1033%26bk%3d1451775130&uiflavor=web&uaid=4c7c1337346b4a0fb64b97945c57a0f4&mkt=EN-US&lc=1033',BC:true,a4:'',A0:true,BD:false,I:false,Ae:false,a5:'',oPost:{},urlFed:'',J:'response_type=token&client_id=000000004017D35F&redirect_url=https://login.live.com/oauth20_desktop.srf&scope=office.onenote&bk=1451775130',Af:0,A1:false,a6:'',BF:"Sign in",K:3,Ah:'',a7:'https://login.live.com/gls.srf?urlID=WinLiveTermsOfUse&mkt=EN-US&vv=1600',A3:false,M:'',BI:'https://auth.gfx.ms/16.000.26014.00/Microsoft_Logotype_Gray.png',A5:true,Ak:false,Al:1,P:'',Am:'##li16####B##Hotmail##/B####BR##The smart way to do email - fast, easy and reliable##li8####B##Messenger##/B####BR##Stay in touch with the most important people in your life##li10####B##SkyDrive##/B####BR##Free, password-protected online storage',A9:'',urlLogin:'https://login.live.com/oauth20_authorize.srf?response_type=token&client_id=000000004017D35F&redirect_url=https://login.live.com/oauth20_desktop.srf&scope=office.onenote&bk=1451775130&mkt=EN-US&lc=1033',Ap:'',av:"#~#partnerdomain#~# does\'t use this service. Please sign in with a Microsoft account or create a new account. <a href=\"#~#WLPaneHelpInviteBlockedURL_LS#~#\" id=\"idPaneHelpInviteBlockedLink9\">Learn More</a>",V:'',ax:"A single-use code lets you sign in without entering your password. This helps protect your account when you\'re using someone else\'s PC. <a href=\"http://explore.live.com/windows-live-sign-in-single-use-code-faq\" id=\"idPaneHelpOTCInfoLink9\" target=\"_blank\">Learn more</a>",aD:'https://login.live.com/gls.srf?urlID=MSNPrivacyStatement&mkt=EN-US&vv=1600',urlFedConvertRename:'https://account.live.com/security/LoginStage.aspx?lmif=1000&ru=https://login.live.com/oauth20_authorize.srf%3Fresponse_type%3Dtoken%26client_id%3D000000004017D35F%26redirect_url%3Dhttps://login.live.com/oauth20_desktop.srf%26scope%3Doffice.onenote%26vv%3D1600%26loginfmt%3Dconctact_lvr%2540live.com%26mkt%3DEN-US%26lc%3D1033&response_type=token&client_id=000000004017D35F&redirect_url=https://login.live.com/oauth20_desktop.srf&scope=office.onenote&vv=1600&loginfmt=conctact_lvr%40live.com&mkt=EN-US&lc=1033',ay:"Your session has timed out. To request a single use code, please <a href=\"javascript:NewOTCRequest()\">refresh the page</a>.",Y:true,Av:{},AA:{},aE:'',aF:2,Ax:'https://sc.imp.live.com/content/dam/imp/surfaces/mail_signin/v3/account/EN-US.html?id=279469&mkt=EN-US',Ay:'https://login.live.com/oauth20_authorize.srf?response_type=token&client_id=000000004017D35F&redirect_url=https://login.live.com/oauth20_desktop.srf&scope=office.onenote&mkt=EN-US&lc=1033&bk=1451775130',aI:true,AE:false,AF:"&copy;2016 Microsoft",sFTTag:'<input type="hidden" name="PPFT" id="i0327" value="DU2pRBKzRt7SzTHFM8igJwfODysa55R!ae1QZKYjexw1LLykan8u*bWZY83DjD37TbjlzVYQFFtP0PKHJ8up07drqwdOKsxwVDsn9n4be5u0oCzUyAEI1srL!hJNzzLCNCSTCyQA0oPQUd*R4!TP6Vfkc5TDWdTxKEV9aGCxstqeQFB28eikXz1M!gVZ7FyK2Q$$"/>',AG:'https://auth.gfx.ms/16.000.26014.00/',sPOST_NewUser:'',AH:0,aL:true,b:'',aO:0,e:'',aP:1033,AM:'sign up',Ba:'',f:'',aS:'',Bc:false,AO:'',Bd:true};</script><script type="text/javascript" src="https://auth.gfx.ms/16.000.26014.00/Login_Strings_JS1033.js"></script>
  <script type="text/javascript" src="https://auth.gfx.ms/16.000.26014.00/Login_Core.js"></script>
  <script type="text/javascript">SRSRetry("__Login_Strings", "https://auth.gfx.ms/16.000.26014.00/Login_Strings_JS1033.js", 1);SRSRetry("__Login_Core", "https://auth.gfx.ms/16.000.26014.00/Login_Core.js", 1);</script><script type="text/javascript">SRSRetry("__Login_Strings", "https://auth.gfx.ms/16.000.26014.00/Login_Strings_JS1033.js", 2);SRSRetry("__Login_Core", "https://auth.gfx.ms/16.000.26014.00/Login_Core.js", 2);</script></head>
<body onload="evt_Login_onload(event);" uiTheme="Web">
</body></html>

And my app's API Settings:

Mobile or desktop client app:

Yes

Restrict JWT issuing:

Yes

Enhanced redirection security:

Enabled

Target domain:

Redirect URLs:

3条回答
三岁会撩人
2楼-- · 2019-09-05 06:34

I believe you are following this sample. Please make sure clientId and Return Uri are correct, scopes are rightly defined and used. A common mistake is discussed in here . Android - look at GitHub sample here. In particular, check the function "authenticateSilent"

查看更多
SAY GOODBYE
3楼-- · 2019-09-05 06:52

The fact is that the authentication process requires a user interaction. It cannot be done by calling directly the url but by loading the url in a browser.

Here is illustrated the 2 steps authentification :

1) Get a temporary code, through user interaction;

2) Get a tolken with that code.

This example uses OkHttp for direct Http calls, Gson for json manipulation. And MozSwing as browser.

private final static Pattern PATTERN_EXTRACT = Pattern.compile(".*code=(.*)$", Pattern.CASE_INSENSITIVE);
private final static String GETCODE_URL = "https://login.live.com/oauth20_authorize.srf?response_type=code&display=touch&client_id=%s&redirect_url=https://login.live.com/oauth20_desktop.srf&scope=office.onenote";

/*
* Step (1) : get a valide code through user interaction
*/
public void requestCode() {

    SwingUtilities.invokeLater(() -> {
        String url = String.format(GETCODE_URL, _the_application_id_);
        browser.load(url);
    });
}

/**
* Called when the browser triggers an event locationChanged()
*/
public void locationChanged() {
    final String location = browser.getUrl();
    Matcher m = PATTERN_EXTRACT.matcher(location);
    if (m.matches() && m.groupCount() == 1) {
        setCode(m.group(1));
    }
}

/**
* Step (2) : get a valid tolken based on the newly aquired code
*/
void setCode(String code) {
    try {

        RequestBody formBody = new FormEncodingBuilder()
                .add("client_id", _the_application_id_)
                .add("redirect_url", TOKEN_REFRESH_REDIRECT_URL)
                .add("client_secret", _the_application_secret_)
                .add("code", code)
                .add("grant_type", "authorization_code")
                .build();

        Request.Builder reqBuilder = new Request.Builder();
        reqBuilder.url(TOKEN_REFRESH_URL);
        reqBuilder.post(formBody);

        Request request = reqBuilder.build();

        final Call call = client.newCall(request);

        // exécution en synchrone
        Response response = call.execute();

        JsonObject refreshTokenResponse = UrlHelper.parseResponse(response);
        final JsonPrimitive at = refreshTokenResponse.getAsJsonPrimitive("access_token");
        if (at != null) {
            setTolken(at.getAsString());
            setStatus(Status.DONE);
            return true;
        }

    } catch (Exception ex) {
        // TODO : add some error handling
        ex.printStackTrace();
    }
}


/**
 * Parse a OneNote HTTP reponse and returns its data as a JSonObject
 *
 * @param response
 *
 * @return
 */
public static JsonObject parseResponse(Response response) {
    final String body;

    try {
        body = response.body().string();
    } catch (IOException ex) {
        throw new InvalidOnenoteContentExcepion("Unable to retrieve response content", ex);
    }

    if (!response.isSuccessful()) {
        if (response.body().contentType().toString().startsWith("application/json")) {
            JsonObject message = ZJsonReader.getInstance().from(body);
            throw new ApiException(message, response.code(), response.message());
        }
        else {
            throw new HttpException(response.code(), response.message());

        }

    }
    if (response.body().contentType().toString().startsWith("application/json")) {
        JsonObject refreshTokenResponse = ZJsonReader.getInstance().from(body);
        return refreshTokenResponse;
    }
    System.err.println(body);
    throw new InvalidOnenoteContentExcepion("Invalid response content :\"" + response.body().contentType().toString() + "\"");
}
查看更多
Juvenile、少年°
4楼-- · 2019-09-05 06:54

Based on Live Connect docs, a desktop app should use response_type=code&display=touch in the query parameters. Can you try response_type=code instead of token?

Also FYI, there is a Live SDK for Android apps that may be easier to use for authenication.

查看更多
登录 后发表回答