{"id":279835,"date":"2016-10-25T05:58:37","date_gmt":"2016-10-25T01:58:37","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=279835"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=279835","title":{"rendered":"\u0421\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e AzureAD \u0434\u043b\u044f Android"},"content":{"rendered":"<p>\u0418\u0442\u0430\u043a, \u0446\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 OAuth 2.0 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 Azure AD API. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0443 \u043d\u0430\u0441 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c, \u0432\u044b\u043d\u043e\u0441\u044f\u0449\u0438\u0439 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043e\u0434\u0430 \u0438\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d.<\/p>\n<p>  \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u044b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 Retrofit, rxJava, retrolambda. \u0418\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u0443\u0441\u043b\u043e\u0432\u043b\u0435\u043d\u043e \u043b\u0438\u0448\u044c \u043c\u043e\u0438\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u0435\u043c \u043c\u0438\u043d\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u0439\u043b\u0435\u0440\u043f\u043b\u0435\u0439\u0442, \u0438 \u043d\u0438\u0447\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435. \u0410 \u043f\u043e\u0442\u043e\u043c\u0443 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u043f\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0443 \u043d\u0430 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0432\u0430\u043d\u0438\u043b\u044c\u043d\u0443\u044e \u0441\u0431\u043e\u0440\u043a\u0443 \u0431\u044b\u0442\u044c \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e.<\/p>\n<p>  \u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u2014 \u043e\u0441\u043e\u0437\u043d\u0430\u0442\u044c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 OAuth 2.0 (\u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e code flow) \u0438 \u043a\u0430\u043a \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043a \u043d\u0430\u0448\u0435\u0439 \u0446\u0435\u043b\u0438:<\/p>\n<p>  <b>1.<\/b> \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d, \u043f\u0435\u0440\u0435\u043f\u0440\u044b\u0433\u0438\u0432\u0430\u0435\u043c \u043d\u0430 \u043f\u0443\u043d\u043a\u0442 4.<\/p>\n<p>  <b>2.<\/b> \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c &#8216;WebView&#8217;, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u0442\u043a\u0440\u043e\u0435\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>  <b>3.<\/b> \u041f\u043e\u0441\u043b\u0435 \u0432\u0432\u043e\u0434\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0438 \u043a\u043b\u0438\u043a\u0430 \u043f\u043e Sign in, \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442 \u043d\u0430 \u0434\u0440\u0443\u0433\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u0432 query parameters \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 code. \u041e\u043d \u0442\u043e \u043d\u0430\u043c \u0438 \u043d\u0443\u0436\u0435\u043d!<\/p>\n<p>  <b>4.<\/b> \u041e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u0435\u043c code \u043d\u0430 \u0442\u043e\u043a\u0435\u043d \u0447\u0435\u0440\u0435\u0437 POST \u0437\u0430\u043f\u0440\u043e\u0441.<br \/>  <a name=\"habracut\"><\/a><br \/>  \u0422\u0435\u043f\u0435\u0440\u044c \u0447\u0442\u043e \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442 \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430?<br \/>  \u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u0443\u0434\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u2014 \u0440\u0430\u0441\u043f\u0438\u0441\u0430\u0442\u044c \u0432 \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0445 \u043a\u043b\u0430\u0441\u0441\u0430\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043d\u0430\u043c \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Endpoints.class<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class Endpoints {     public static final String OAUTH2_BASE_URL = &quot;https:\/\/login.microsoftonline.com&quot;;     public static final String OAUTH2_ENDPOINT = &quot;\/oauth2&quot;;     public static final String OAUTH2_AUTHORIZATION_ENDPOINT = &quot;\/authorize&quot;;     public static final String OAUTH2_TOKEN_ENDPOINT = &quot;\/token&quot;;     public static final String OAUTH2_TENANT_PATH_FIELD = &quot;\/{tenant}&quot;; } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">QueryFields.class<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class QueryFields {     public static final String QUERY_OAUTH2_CLIENT_ID = &quot;client_id&quot;;     public static final String QUERY_OAUTH2_RESPONSE_TYPE = &quot;response_type&quot;;     public static final String QUERY_OAUTH2_REDIRECT_URI = &quot;redirect_uri&quot;;     public static final String QUERY_OAUTH2_RESOURCE = &quot;resource&quot;; } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">RequestFields.class<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class RequestFields {     public static final String OAUTH2_CLIENT_ID = &quot;client_id&quot;;     public static final String OAUTH2_GRANT_TYPE = &quot;grant_type&quot;;     public static final String OAUTH2_RESOURCE = &quot;resource&quot;;     public static final String OAUTH2_CODE = &quot;code&quot;;     public static final String OAUTH2_REDIRECT_URI = &quot;redirect_uri&quot;;     public static final String OAUTH2_RAW_CODE_QUERY_FIELD = &quot;?code&quot;;     public static final String OAUTH2_CODE_QUERY_FIELD = &quot;code&quot;;     public static final String OAUTH2_RAW_QEURY_ERROR_FIELD = &quot;error=&quot;; } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">RequestFieldValues.class<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class RequestFieldValues {     public static final String TENANT_COMMON = &quot;common&quot;;     public static final String GRANT_TYPE_REFRESH_TOKEN = &quot;refresh_token&quot;; } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">ResponseFields.class<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class ResponseFields {     public static final String OAUTH2_TOKEN_TYPE = &quot;token_type&quot;;     public static final String OAUTH2_TOKEN_EXPIRES_IN = &quot;expires_in&quot;;     public static final String OAUTH2_TOKEN_SCOPE = &quot;scope&quot;;     public static final String OAUTH2_TOKEN_EXPIRES_ON = &quot;expires_on&quot;;     public static final String OAUTH2_TOKEN_NOT_BEFORE = &quot;not_before&quot;;     public static final String OAUTH2_TOKEN_RESOURCE = &quot;resource&quot;;     public static final String OAUTH2_TOKEN_ACCESS_TOKEN = &quot;access_token&quot;;     public static final String OAUTH2_TOKEN_REFRESH_TOKEN = &quot;refresh_token&quot;;     public static final String OAUTH2_TOKEN_ID_TOKEN = &quot;id_token&quot;; } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041d\u0430\u0437\u043d\u0430\u0447\u0438\u043c \u0437\u0430\u043e\u0434\u043d\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u043e\u0433\u043e OkHttp-\u043a\u043b\u0438\u0435\u043d\u0442\u0430:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Const.class<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class Const {     public static int CONNECT_TIMEOUT = 15;     public static int WRITE_TIMEOUT = 60;     public static int TIMEOUT = 60; } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u0434\u0435\u043b\u0443. \u041f\u043e \u0444\u0430\u043a\u0442\u0443, \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0432\u0430\u0436\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043d\u0430\u0448\u0435\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u0442\u044c \u0438\u0437 \u0434\u0432\u0443\u0445 \u0444\u0430\u0439\u043b\u043e\u0432 \u2014 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <code>OAuth2<\/code>, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u044b \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u0438 \u0444\u0430\u0431\u0440\u0438\u043a\u0443 API, \u0438 <code>OAuth2WebViewClient<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043f\u043e\u0434 \u043d\u0430\u0448\u0438 \u043d\u0443\u0436\u0434\u044b WebViewClient.<\/p>\n<p>  \u041d\u0430\u0447\u043d\u0435\u043c \u043f\u043e \u043f\u043e\u0440\u044f\u0434\u043a\u0443.<\/p>\n<p>  \u0421\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u044b \u043e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0439 \u0434\u043b\u044f \u043e\u0431\u043c\u0435\u043d\u0430 code \u043d\u0430 token \u0432\u044b\u0433\u043b\u044f\u0434\u044f\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"java\">    @FormUrlEncoded     @POST(OAUTH2_TENANT_PATH_FIELD + OAUTH2_ENDPOINT + OAUTH2_TOKEN_ENDPOINT)     Observable&lt;Response&lt;Token&gt;&gt; tradeCodeForToken(         @Path(OAUTH2_TENANT_PATH_FIELD) String tenant,         @Field(OAUTH2_CLIENT_ID) String clientId,         @Field(OAUTH2_GRANT_TYPE) String grantType,         @Field(OAUTH2_RESOURCE) String resource,         @Field(OAUTH2_CODE) String code,         @Field(OAUTH2_REDIRECT_URI) String redirectUri     ); <\/code><\/pre>\n<p>  <\/p>\n<pre><code class=\"java\">    @FormUrlEncoded     @POST(OAUTH2_TENANT_PATH_FIELD + OAUTH2_ENDPOINT + OAUTH2_TOKEN_ENDPOINT)     Observable&lt;Response&lt;Token&gt;&gt; refreshToken(             @Path(OAUTH2_TENANT_PATH_FIELD) String tenant,             @Field(OAUTH2_CLIENT_ID) String clientId,             @Field(OAUTH2_GRANT_TYPE) String grantType,             @Field(OAUTH2_RESOURCE) String resource,             @Field(OAUTH2_TOKEN_REFRESH_TOKEN) String refreshToken,             @Field(OAUTH2_REDIRECT_URI) String redirectUri     ); <\/code><\/pre>\n<p>  \u0417\u0434\u0435\u0441\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 \u2014 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0430 \u0437\u0430\u043f\u0440\u043e\u0441\u0430, \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0433\u043e \u0432 \u043f\u0443\u043d\u043a\u0442\u0435 4, \u0430 \u0432\u0442\u043e\u0440\u043e\u0439 \u2014 \u0440\u0435\u0444\u0440\u0435\u0448 \u0442\u043e\u043a\u0435\u043d\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0442\u0440\u0435\u0431\u043e\u0432\u0430\u0442\u044c\u0441\u044f, \u0442\u0430\u043a \u043a\u0430\u043a \u0442\u043e\u043a\u0435\u043d \u0441\u0435\u0441\u0441\u0438\u0438 \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0432\u0430\u043b\u0438\u0434\u0435\u043d \u0432 \u0442\u0435\u0447\u0435\u043d\u0438\u0435 \u0447\u0430\u0441\u0430.<\/p>\n<p>  \u0422\u0435\u043f\u0435\u0440\u044c \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0444\u0430\u0431\u0440\u0438\u043a\u0438 API. \u0418\u0442\u0430\u043a, \u0447\u0442\u043e \u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c? \u0417\u0430 \u0432\u0440\u0435\u043c\u044f \u043c\u043e\u0435\u0439 \u0442\u0435\u0441\u043d\u043e\u0439 \u0434\u0440\u0443\u0436\u0431\u044b \u0441 Retrofit-\u043e\u043c \u044f \u043f\u0440\u0438\u0448\u0435\u043b \u043a \u0434\u0430\u043d\u043d\u043e\u043c\u0443 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0443 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0441\u0435\u0433\u043e \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430:<\/p>\n<pre><code class=\"java\">class Factory {         public static OAuth2 buildOAuth2API(boolean enableDebug) {             return buildRetrofit(OAUTH2_BASE_URL, enableDebug).create(OAuth2.class);         }         protected static Retrofit buildRetrofit(String baseUrl, boolean enableDebug) {             return new Retrofit.Builder()                     .baseUrl(baseUrl)                     .addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))                     .addCallAdapterFactory(RxJavaCallAdapterFactory.create())                     .client(buildClient(enableDebug))                     .build();         }         protected static OkHttpClient buildClient(boolean enableDebug) {             OkHttpClient.Builder builder = new OkHttpClient.Builder()                     .connectTimeout(Const.CONNECT_TIMEOUT, TimeUnit.SECONDS)                     .writeTimeout(Const.WRITE_TIMEOUT, TimeUnit.SECONDS)                     .readTimeout(Const.TIMEOUT, TimeUnit.SECONDS);             if(enableDebug) {                 builder.addInterceptor(                             new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)                         );             }             return builder.build();         }     } <\/code><\/pre>\n<p>  \u0414\u0430\u043d\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0440\u0430\u043d\u0435\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u043c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0435.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">\u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u043f\u043e\u0434 \u043a\u0430\u0442\u043e\u043c<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code>public interface OAuth2 {     \/** The request signature that returns a deserialized token *\/     @FormUrlEncoded     @POST(OAUTH2_TENANT_PATH_FIELD + OAUTH2_ENDPOINT + OAUTH2_TOKEN_ENDPOINT)     Observable&lt;Response&lt;Token&gt;&gt; tradeCodeForToken(         @Path(OAUTH2_TENANT_PATH_FIELD) String tenant,         @Field(OAUTH2_CLIENT_ID) String clientId,         @Field(OAUTH2_GRANT_TYPE) String grantType,         @Field(OAUTH2_RESOURCE) String resource,         @Field(OAUTH2_CODE) String code,         @Field(OAUTH2_REDIRECT_URI) String redirectUri     );     \/** The request signature that returns a raw json object instead of deserealized token *\/     @FormUrlEncoded     @POST(OAUTH2_TENANT_PATH_FIELD + OAUTH2_ENDPOINT + OAUTH2_TOKEN_ENDPOINT)     Observable&lt;Response&lt;JsonObject&gt;&gt; tradeCodeForTokenRaw(             @Path(OAUTH2_TENANT_PATH_FIELD) String tenant,             @Field(OAUTH2_CLIENT_ID) String clientId,             @Field(OAUTH2_GRANT_TYPE) String grantType,             @Field(OAUTH2_RESOURCE) String resource,             @Field(OAUTH2_CODE) String code,             @Field(OAUTH2_REDIRECT_URI) String redirectUri     );     \/** The request signature that allows refreshing token *\/     @FormUrlEncoded     @POST(OAUTH2_TENANT_PATH_FIELD + OAUTH2_ENDPOINT + OAUTH2_TOKEN_ENDPOINT)     Observable&lt;Response&lt;Token&gt;&gt; refreshToken(             @Path(OAUTH2_TENANT_PATH_FIELD) String tenant,             @Field(OAUTH2_CLIENT_ID) String clientId,             @Field(OAUTH2_GRANT_TYPE) String grantType,             @Field(OAUTH2_RESOURCE) String resource,             @Field(OAUTH2_TOKEN_REFRESH_TOKEN) String refreshToken,             @Field(OAUTH2_REDIRECT_URI) String redirectUri     );     \/** The request signature that allows refreshing token and returns a raw json instead of deserialized token *\/     @FormUrlEncoded     @POST(OAUTH2_TENANT_PATH_FIELD + OAUTH2_ENDPOINT + OAUTH2_TOKEN_ENDPOINT)     Observable&lt;Response&lt;Token&gt;&gt; refreshTokenRaw(             @Path(OAUTH2_TENANT_PATH_FIELD) String tenant,             @Field(OAUTH2_CLIENT_ID) String clientId,             @Field(OAUTH2_GRANT_TYPE) String grantType,             @Field(OAUTH2_RESOURCE) String resource,             @Field(OAUTH2_TOKEN_REFRESH_TOKEN) String refreshToken,             @Field(OAUTH2_REDIRECT_URI) String redirectUri     );     class Factory {         public static OAuth2 buildOAuth2API(boolean enableDebug) {             return buildRetrofit(OAUTH2_BASE_URL, enableDebug).create(OAuth2.class);         }         protected static Retrofit buildRetrofit(String baseUrl, boolean enableDebug) {             return new Retrofit.Builder()                     .baseUrl(baseUrl)                     .addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))                     .addCallAdapterFactory(RxJavaCallAdapterFactory.create())                     .client(buildClient(enableDebug))                     .build();         }         protected static OkHttpClient buildClient(boolean enableDebug) {             OkHttpClient.Builder builder = new OkHttpClient.Builder()                     .connectTimeout(Const.CONNECT_TIMEOUT, TimeUnit.SECONDS)                     .writeTimeout(Const.WRITE_TIMEOUT, TimeUnit.SECONDS)                     .readTimeout(Const.TIMEOUT, TimeUnit.SECONDS);             if(enableDebug) {                  builder.addInterceptor(                             new HttpLoggingInterceptor().setLevel(                                     HttpLoggingInterceptor.Level.BODY                                 )                         );             }             return builder.build();         }     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">Token DTO<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class Token {     @SerializedName(OAUTH2_TOKEN_TYPE)     private String tokenType;     @SerializedName(OAUTH2_TOKEN_EXPIRES_IN)     private String expiresIn;     @SerializedName(OAUTH2_TOKEN_SCOPE)     private String scope;     @SerializedName(OAUTH2_TOKEN_EXPIRES_ON)     private String expiresOn;     @SerializedName(OAUTH2_TOKEN_NOT_BEFORE)     private String notBefore;     @SerializedName(OAUTH2_TOKEN_RESOURCE)     private String resource;     @SerializedName(OAUTH2_TOKEN_ACCESS_TOKEN)     private String accessToken;     @SerializedName(OAUTH2_TOKEN_REFRESH_TOKEN)     private String refreshToken;     @SerializedName(OAUTH2_TOKEN_ID_TOKEN)     private String idToken;      public Token(String tokenType, String expiresIn, String scope, String expiresOn, String notBefore, String resource, String accessToken, String refreshToken, String idToken) {         this.tokenType = tokenType;         this.expiresIn = expiresIn;         this.scope = scope;         this.expiresOn = expiresOn;         this.notBefore = notBefore;         this.resource = resource;         this.accessToken = accessToken;         this.refreshToken = refreshToken;         this.idToken = idToken;     }     public String getTokenType() {         return tokenType;     }     public void setTokenType(String tokenType) {         this.tokenType = tokenType;     }     public String getExpiresIn() {         return expiresIn;     }     public void setExpiresIn(String expiresIn) {         this.expiresIn = expiresIn;     }     public String getScope() {         return scope;     }     public void setScope(String scope) {         this.scope = scope;     }     public String getExpiresOn() {         return expiresOn;     }     public void setExpiresOn(String expiresOn) {         this.expiresOn = expiresOn;     }     public String getNotBefore() {         return notBefore;     }     public void setNotBefore(String notBefore) {         this.notBefore = notBefore;     }     public String getResource() {         return resource;     }     public void setResource(String resource) {         this.resource = resource;     }     public String getAccessToken() {         return accessToken;     }     public void setAccessToken(String accessToken) {         this.accessToken = accessToken;     }     public String getRefreshToken() {         return refreshToken;     }     public void setRefreshToken(String refreshToken) {         this.refreshToken = refreshToken;     }     public String getIdToken() {         return idToken;     }     public void setIdToken(String idToken) {         this.idToken = idToken;     }     @Override     public String toString() {         return &quot;MicrosoftAzureOAuthToken{&quot; +                 &quot;tokenType='&quot; + tokenType + '\\'' +                 &quot;, expiresIn='&quot; + expiresIn + '\\'' +                 &quot;, scope='&quot; + scope + '\\'' +                 &quot;, expiresOn='&quot; + expiresOn + '\\'' +                 &quot;, notBefore='&quot; + notBefore + '\\'' +                 &quot;, resource='&quot; + resource + '\\'' +                 &quot;, accessToken='&quot; + accessToken + '\\'' +                 &quot;, refreshToken='&quot; + refreshToken + '\\'' +                 &quot;, idToken='&quot; + idToken + '\\'' +                 '}';     }     public String toJsonString() {         return new Gson().toJson(this, Token.class);     }     public static Token fromJsonString(String jsonString) {         return new Gson().fromJson(jsonString, Token.class);     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c \u043a \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u0430\u0441\u0442\u043e\u043c\u043d\u043e\u0433\u043e WebViewClient-\u0430. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f, \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c. \u041f\u043e \u0444\u0430\u043a\u0442\u0443, \u043d\u0430 \u0432\u0445\u043e\u0434 \u043f\u0440\u0438 \u0435\u0433\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0441\u0441\u044b\u043b\u043a\u0438 \u043d\u0430 callback-\u0438, \u0438\u043b\u0438 \u043d\u0430 BehaviourSubject-\u044b (\u043f\u043e \u0432\u043a\u0443\u0441\u0443, \u043c\u043d\u0435 \u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f \u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043f\u0435\u0440\u0432\u043e\u0435). \u0412\u0441\u0435\u0433\u043e \u0438\u0445 \u0431\u0443\u0434\u0435\u0442 \u0442\u0440\u0438: \u043f\u0435\u0440\u0432\u044b\u0439 \u2014 \u0431\u0443\u0434\u0435\u0442 \u0442\u0440\u0438\u0433\u0433\u0435\u0440\u0438\u0442\u044c\u0441\u044f \u043f\u0440\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u043e\u043c \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u0438 \u043a\u043e\u0434\u0430, \u0432\u0442\u043e\u0440\u043e\u0439 \u2014 \u043f\u0440\u0438 \u043d\u0430\u043b\u0438\u0447\u0438\u0438 &#8216;error=&#8217; \u043f\u043e\u0434\u0441\u0442\u0440\u043e\u043a\u0438 \u0432 url \u043f\u043e\u0441\u043b\u0435 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442\u0430 \u0438 \u0442\u0440\u0435\u0442\u0438\u0439 \u2014 \u0441\u043b\u0443\u0448\u0430\u044e\u0449\u0438\u0439 \u0432\u0441\u0435 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u044b.<\/p>\n<p>  \u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0434\u0432\u0430 \u043c\u0435\u0442\u043e\u0434\u0430 <code>WebViewClient<\/code>: <code>shouldOverrideUrlLoading(WebView webView, String url)<\/code>\u0438 <code>onPageFinished(WebView webView, String url)<\/code>.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">OAuth2WebViewClient<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class OAuth2WebViewClient extends WebViewClient {     private Action1&lt;String&gt; onSuccess;     private Action1&lt;String&gt; onError;     private Action1&lt;String&gt; onUnknownUrlPassed;     public OAuth2WebViewClient(Action1&lt;String&gt; onSuccess, Action1&lt;String&gt; onError, Action1&lt;String&gt; onUnknownUrlPassed) {         this.onSuccess = onSuccess;         this.onUnknownUrlPassed = onUnknownUrlPassed;         this.onError = onError;     }     @Override     public boolean shouldOverrideUrlLoading(WebView view, String url) {         if(url.contains(OAUTH2_RAW_CODE_QUERY_FIELD) || url.contains(OAUTH2_RAW_QEURY_ERROR_FIELD)) {             return true;         } else {             view.loadUrl(url);             return false;         }     }     @Override     public void onPageFinished(WebView view, String url) {         super.onPageFinished(view, url);         if(url.contains(OAUTH2_RAW_CODE_QUERY_FIELD)) {             Uri uri = Uri.parse(url);             onSuccess.call(uri.getQueryParameter(OAUTH2_CODE_QUERY_FIELD));         } else if(url.contains(OAUTH2_RAW_CODE_QUERY_FIELD)) {             onError.call(url);         } else {             onUnknownUrlPassed.call(url);         }     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u041f\u043e \u0444\u0430\u043a\u0442\u0443 \u2014 \u0432\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e \u0434\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u043d\u043e \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0433\u0438\u0431\u043a\u043e\u0433\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u0430 \u0435\u0449\u0435 \u043f\u0430\u0440\u0443 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u0431\u043e\u0439\u043b\u0435\u0440\u043f\u043b\u0435\u0439\u0442 \u0435\u0449\u0435 \u043d\u0430 \u0447\u0443\u0442\u044c-\u0447\u0443\u0442\u044c.<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">AzureAuthenticationWebView<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class AzureAuthenticationWebView extends WebView {     public AzureAuthenticationWebView(Context context) {         super(context);     }     public AzureAuthenticationWebView(Context context, AttributeSet attrs) {         super(context, attrs);     }     public AzureAuthenticationWebView(Context context, AttributeSet attrs, int defStyleAttr) {         super(context, attrs, defStyleAttr);     }     @TargetApi(Build.VERSION_CODES.LOLLIPOP)     public AzureAuthenticationWebView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {         super(context, attrs, defStyleAttr, defStyleRes);     }     public void init(OAuth2WebViewClient client, String query) {         WebSettings settings = this.getSettings();         settings.setJavaScriptEnabled(true);         settings.setSupportMultipleWindows(true);         this.setWebViewClient(client);         this.loadUrl(query);     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">AzureStorageManager<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class AzureStorageManager {     private ObscuredSharedPreferences preferences;     public AzureStorageManager(ObscuredSharedPreferences preferences) {         this.preferences = preferences;     }     public Token readToken() {         String rawToken = preferences.getString(TOKEN_JSON_KEY, &quot;&quot;);         return Token.fromJsonString(rawToken);     }     public void writeToken(Token token) {         ObscuredSharedPreferences.Editor editor = preferences.edit();         editor.putString(TOKEN_JSON_KEY, token.toJsonString());         editor.commit();     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  <\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">QueryStringBuilder<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class QueryStringBuilder {     private String query;     public QueryStringBuilder(String tenant) {         query = OAUTH2_BASE_URL.concat(&quot;\/&quot;).concat(tenant).concat(OAUTH2_ENDPOINT).concat(OAUTH2_AUTHORIZATION_ENDPOINT).concat(&quot;?&quot;);     }     public QueryStringBuilder setClientId(String clientId) {         query = prepareQuery(query);         query = query.concat(QUERY_OAUTH2_CLIENT_ID).concat(&quot;=&quot;).concat(clientId);         return this;     }     public QueryStringBuilder setResponseType(String responseType) {         query = prepareQuery(query);         query = query.concat(QUERY_OAUTH2_RESPONSE_TYPE).concat(&quot;=&quot;).concat(responseType);         return this;     }     public QueryStringBuilder setRedirectUri(String redirectUri) {         query = prepareQuery(query);         query = query.concat(QUERY_OAUTH2_REDIRECT_URI).concat(&quot;=&quot;).concat(redirectUri);         return this;     }     public QueryStringBuilder setResource(String resource) {         query = prepareQuery(query);         query = query.concat(QUERY_OAUTH2_RESOURCE).concat(&quot;=&quot;).concat(resource);         return this;     }     public String build() {         return query;     }     private String prepareQuery(String query) {         if(query != null && query.length() != 0 && !(String.valueOf(query.charAt(query.length() - 1)).equals(&quot;?&quot;))) {             query = query.concat(&quot;&&quot;);         }         return query;     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0412 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435, \u043d\u0430 \u044d\u0442\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\u0441\u044f, \u0435\u0441\u043b\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438, \u043d\u043e \u043c\u043d\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u043c\u0435\u0441\u0442\u0435\u043d \u0442\u0430\u043a\u0436\u0435 \u043c\u0435\u043d\u0435\u0434\u0436\u0435\u0440 \u0442\u043e\u043a\u0435\u043d\u043e\u0432, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u0447\u0435\u043d\u044c \u0443\u0436 \u0447\u0430\u0441\u0442\u043e \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u043b\u043e\u0441\u044c \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0438 \u0441 \u0442\u043e\u043a\u0435\u043d\u0430\u043c\u0438. \u0410 \u043f\u043e\u0442\u043e\u043c\u0443, \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0431\u043e\u043d\u0443\u0441\u0430 \u0438\u0434\u0435\u0442 \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043a\u043b\u0430\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432 \u0434\u043e\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0442\u043e\u043a\u0435\u043d\u043e\u0432, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0440\u0435\u0444\u0440\u0435\u0448. \u0412\u0443\u0430\u043b\u044f:<\/p>\n<div class=\"spoiler\"><b class=\"spoiler_title\">TokenManager<\/b><\/p>\n<div class=\"spoiler_text\">\n<pre><code class=\"java\">public class TokenManager {     private Subscription subscription = Subscriptions.empty();     private AzureStorageManager storageManager;     private String tenantType;     private String clientId;     private String redirectUri;     public TokenManager(AzureStorageManager storageManager, String tenantType, String clientId, String redirectUri) {         this.storageManager = storageManager;         this.tenantType = tenantType;         this.clientId = clientId;         this.redirectUri = redirectUri;     }     \/** Performs (code -&gt; token) exchange using MS OAuth2 API       * Caches the token if the response code is equals to HTTP_OK *\/     public void tradeCodeForToken(String code, String resource, final Action1&lt;Token&gt; onSuccess, Action1&lt;Integer&gt; onHttpError, Action1&lt;Throwable&gt; onFailure) {         subscription = OAuth2.Factory.buildOAuth2API(false)                 .tradeCodeForToken(                         tenantType,                         clientId,                         GRANT_TYPE_REFRESH_TOKEN,                         resource,                         code,                         redirectUri                 )                 .subscribeOn(Schedulers.io())                 .observeOn(AndroidSchedulers.mainThread())                 .filter(response -&gt; {                     if(response.code() != HTTP_OK) {                         onHttpError.call(response.code());                         return false;                     }                     return true;                 })                 .map(Response::body)                 .subscribe(                         token -&gt; {                             storageManager.writeToken(token);                             onSuccess.call(token);                         },                         e -&gt; {                             onFailure.call(e);                             subscription.unsubscribe();                         },                         () -&gt; subscription.unsubscribe()                 );     }     \/** Refreshes expired token       * Caches the token if the response code is equals to HTTP_OK *\/     public void refreshToken(Token expiredToken, final Action1&lt;Token&gt; onSuccess, Action1&lt;Integer&gt; onHttpError, Action1&lt;Throwable&gt; onFailure) {         subscription = OAuth2.Factory.buildOAuth2API(false)                 .refreshToken(                         tenantType,                         clientId,                         GRANT_TYPE_REFRESH_TOKEN,                         expiredToken.getResource(),                         expiredToken.getRefreshToken(),                         redirectUri                 )                 .subscribeOn(Schedulers.io())                 .observeOn(AndroidSchedulers.mainThread())                 .filter(response -&gt; {                     if(response.code() != HTTP_OK) {                         onHttpError.call(response.code());                         return false;                     }                     return true;                 })                 .map(Response::body)                 .subscribe(                         token -&gt; {                             storageManager.writeToken(token);                             onSuccess.call(token);                         },                         e -&gt; {                             onFailure.call(e);                             subscription.unsubscribe();                         },                         () -&gt; subscription.unsubscribe()                 );     } } <\/code><\/pre>\n<p>  <\/div>\n<\/div>\n<p>  \u0412\u043e\u0442 \u0438 \u0432\u0441\u0435, \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u0430\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0433\u043e\u0442\u043e\u0432\u0430. \u041e\u043d\u0430 \u043b\u0435\u0433\u043a\u043e \u043a\u0430\u0441\u0442\u043e\u043c\u0438\u0437\u0438\u0440\u0443\u0435\u043c\u0430, \u0438, \u0447\u0442\u043e \u0441\u0430\u043c\u043e\u0435 \u0433\u043b\u0430\u0432\u043d\u043e\u0435 \u2014 \u043e\u043d\u0430 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442! <\/p>\n<p>  \u041d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043f\u0440\u0438\u043c\u0435\u0447\u0430\u043d\u0438\u0435 \u2014 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435, \u0435\u0441\u043b\u0438 \u0432\u044b \u0437\u0430\u0445\u043e\u0442\u0438\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c WebView \u0432 \u0434\u0438\u0430\u043b\u043e\u0433\u0435 \u2014 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0432\u044b\u0441\u0442\u0430\u0432\u044c\u0442\u0435 \u0435\u0439 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u0443\u044e \u0432\u044b\u0441\u043e\u0442\u0443, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u043d\u0430 \u043f\u0440\u043e\u0441\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u043d\u0443\u043b\u0435\u0432\u0443\u044e \u0432\u044b\u0441\u043e\u0442\u0443.<\/p>\n<p>  \u0421\u0442\u0430\u0442\u044c\u044f \u0431\u044b\u043b\u0430 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u0430 \u043f\u043e \u043c\u043e\u0442\u0438\u0432\u0430\u043c \u043c\u043e\u0435\u0439 \u043a\u0443\u0440\u0441\u043e\u0432\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u044f \u0434\u0435\u043b\u0430\u044e \u043d\u0430 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442, \u0432 \u0441\u0432\u044f\u0437\u0438 \u0441 \u0442\u0435\u043c \u0447\u0442\u043e \u044f \u043e\u0436\u0438\u0434\u0430\u044e, \u043f\u043e\u043a\u0430 \u043c\u043d\u0435 \u0432\u044b\u0434\u0430\u0434\u0443\u0442 \u0430\u043a\u043a\u0430\u0443\u043d\u0442 Azure AD, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u0435\u043b\u0435\u0433\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f\u043c. \u0412 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0431\u0443\u0434\u0435\u0442 \u0435\u0449\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u0442\u0430\u0442\u0435\u0439, \u043f\u043e\u0441\u0432\u044f\u0449\u0435\u043d\u043d\u044b\u0445 \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 OneNote for Business API (\u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u2014 \u0441 classNotebooks \u0441\u0435\u043a\u0446\u0438\u0435\u0439 \u0438\u0445 api).<\/p>\n<p>  \u041d\u0430 \u044d\u0442\u043e\u043c \u0432\u0441\u0435. \u0411\u0443\u0434\u0443 \u043f\u0440\u0438\u0437\u043d\u0430\u0442\u0435\u043b\u0435\u043d \u0437\u0430 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u0438\u0432\u043d\u0443\u044e \u043a\u0440\u0438\u0442\u0438\u043a\u0443, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0443 \u0440\u0430\u0434 \u043e\u0442\u0432\u0435\u0442\u0438\u0442\u044c \u043d\u0430 \u0432\u0430\u0448\u0438 \u0432\u043e\u043f\u0440\u043e\u0441\u044b.<br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habrahabr.ru\/post\/313454\/\"> https:\/\/habrahabr.ru\/post\/313454\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u0418\u0442\u0430\u043a, \u0446\u0435\u043b\u044c \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u2014 \u043f\u043e\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 OAuth 2.0 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u0447\u0435\u0440\u0435\u0437 Azure AD API. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0443 \u043d\u0430\u0441 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c, \u0432\u044b\u043d\u043e\u0441\u044f\u0449\u0438\u0439 \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043a\u043e\u0434\u0430 \u0438\u0437 \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043a \u043a\u043e\u0442\u043e\u0440\u043e\u043c\u0443 \u043e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d.<\/p>\n<p>  \u0412 \u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u044b \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 Retrofit, rxJava, retrolambda. \u0418\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u0443\u0441\u043b\u043e\u0432\u043b\u0435\u043d\u043e \u043b\u0438\u0448\u044c \u043c\u043e\u0438\u043c \u0436\u0435\u043b\u0430\u043d\u0438\u0435\u043c \u043c\u0438\u043d\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u0439\u043b\u0435\u0440\u043f\u043b\u0435\u0439\u0442, \u0438 \u043d\u0438\u0447\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0435. \u0410 \u043f\u043e\u0442\u043e\u043c\u0443 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u043f\u043e \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0443 \u043d\u0430 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0432\u0430\u043d\u0438\u043b\u044c\u043d\u0443\u044e \u0441\u0431\u043e\u0440\u043a\u0443 \u0431\u044b\u0442\u044c \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e.<\/p>\n<p>  \u041f\u0435\u0440\u0432\u043e\u0435, \u0447\u0442\u043e \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u2014 \u043e\u0441\u043e\u0437\u043d\u0430\u0442\u044c, \u0447\u0442\u043e \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0441\u043e\u0431\u043e\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 OAuth 2.0 (\u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0441\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e code flow) \u0438 \u043a\u0430\u043a \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043a \u043d\u0430\u0448\u0435\u0439 \u0446\u0435\u043b\u0438:<\/p>\n<p>  <b>1.<\/b> \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043a\u044d\u0448\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u0442\u043e\u043a\u0435\u043d, \u043f\u0435\u0440\u0435\u043f\u0440\u044b\u0433\u0438\u0432\u0430\u0435\u043c \u043d\u0430 \u043f\u0443\u043d\u043a\u0442 4.<\/p>\n<p>  <b>2.<\/b> \u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c &#8216;WebView&#8217;, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u0442\u043a\u0440\u043e\u0435\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0430\u0432\u0442\u043e\u0440\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>  <b>3.<\/b> \u041f\u043e\u0441\u043b\u0435 \u0432\u0432\u043e\u0434\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u043c \u0438 \u043a\u043b\u0438\u043a\u0430 \u043f\u043e Sign in, \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0440\u0435\u0434\u0438\u0440\u0435\u043a\u0442 \u043d\u0430 \u0434\u0440\u0443\u0433\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u0432 query parameters \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0438\u043c\u0435\u0435\u0442\u0441\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 code. \u041e\u043d \u0442\u043e \u043d\u0430\u043c \u0438 \u043d\u0443\u0436\u0435\u043d!<\/p>\n<p>  <b>4.<\/b> \u041e\u0431\u043c\u0435\u043d\u0438\u0432\u0430\u0435\u043c code \u043d\u0430 \u0442\u043e\u043a\u0435\u043d \u0447\u0435\u0440\u0435\u0437 POST \u0437\u0430\u043f\u0440\u043e\u0441.  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-279835","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/279835","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=279835"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/279835\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=279835"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=279835"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=279835"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}