Android授权代码流程中的oauth2

萧德馨
2023-12-01

OAuth is an open standard for secure authentication, commonly used to grant websites or applications access to information on other platforms without giving them the passwords.

OAuth是安全身份验证的开放标准,通常用于授予网站或应用程序访问其他平台上的信息的权限,而无需为其提供密码。

This article shows the technical implementation of an OAuth2 Authentication on Android, using the Authorization Code Flow. It uses Twitch as OAuth Provider but it can be applied to any other API following the OAuth2 standard.

本文使用授权代码 展示了Android上OAuth2身份验证的技术实现。 它使用Twitch作为OAuth Provider,但可以将其应用于遵循OAuth2标准的 任何其他API

To learn about the structure and reasoning behind the OAuth standard please check out this great post by Takahiko Kawasaki.To learn about all the different OAuth2 Authorization Flows and its steps check out this awesome article by that same author.

要了解OAuth标准背后的结构和推理,请查看Takahiko Kawasaki的 精彩文章 。要了解所有不同的OAuth2 授权流程及其步骤, 请查看同一位作者的精彩文章

总览 (Overview)

This Article is divided into the following parts:

本文分为以下几部分:

  1. Obtain an authorization code from the OAuth provider using a WebView.

    使用WebView从OAuth提供程序获取授权代码

  2. Use the previous code to obtain the access token and refresh token with a networking library (Ktor).

    使用前面的代码来获取访问令牌并使用网络库( Ktor ) 刷新令牌

  3. [Recurring]: When the access token expires, use the refresh token to obtain a new one, or redirect the user to step 1.

    [重复出现]:当访问令牌过期时,请使用刷新令牌来获取一个新令牌,或者将用户重定向到步骤1。

第1部分。获取授权码 (Part 1. Getting the Authorization Code)

The authorization code is obtained through a URL request that can be used in any regular browser.

授权代码是通过可在任何常规浏览器中使用的URL请求获得的。

Example OAuth2 Authorization Code Request
OAuth2授权码示例示例

First, prepare the different parameters needed, then build the URL and finally catch the redirect inside the WebView.

首先,准备所需的不同参数,然后构建URL,最后在WebView中捕获重定向。

获取参数 (Obtaining the parameters)

Register a new application on your OAuth provider site to obtain and configure the following parameters:

在OAuth提供者网站上注册新的应用程序,以获取并配置以下参数:

  • Client ID: A unique string identifier generated by the OAuth provider to identify your application.

    客户端ID :由OAuth提供程序生成的唯一字符串标识符,用于标识您的应用程序。

  • Client Secret: A unique string identifier passed to the token exchange endpoints to obtain the tokens. This will be used in the next part of the article.

    客户机密 :唯一的字符串标识符,传递给令牌交换端点以获得令牌。 这将在本文的下一部分中使用。

  • Redirect Uri: After the user logs in, the provider will redirect him to this website with the code as a parameter in the URL. For mobile apps, you can use any value, for example http://localhost since it will be handled manually on the WebView.

    重定向Uri :用户登录后,提供商将使用代码作为URL中的参数将其重定向到此网站。 对于移动应用程序,可以使用任何值,例如http://localhost因为它将在WebView上手动处理。

Find the remaining variables from your provider’s documentation:

从提供商的文档中找到其余的变量:

  • Authorization URL: The endpoint URL exposed by the OAuth provider to get the authorization code. For example https://id.twitch.tv/oauth2/authorize.

    授权URL :OAuth提供程序公开以获取授权代码的端点URL。 例如 https://id.twitch.tv/oauth2/authorize

  • Scopes: A space-separated list containing all the permissions to request. For example user:read:email user:edit.

    范围:以空格分隔的列表,其中包含所有请求权限。 例如 user:read:email user:edit

创建URL请求 (Creating the URL Request)

Back to Android Studio, add a WebView in your app that will manage the authentication.

返回Android Studio,在您的应用中添加一个WebView来管理身份验证。

Create a unique string variable state, used to avoid CSRF attacks. For every request, include it and check that it is echoed back on the response from the provider.

创建一个唯一的字符串变量state ,用于避免CSRF攻击 。 对于每个请求,都应包含它,并检查它是否在提供程序的响应中回显。

Build a Uri object with the parameters obtained in the previous steps.

使用前面步骤中获得的参数构建一个Uri对象。

OAuth2 Authorization Code Uri
OAuth2授权码Uri

Load the URI onto your WebView to start the Authentication process.

将URI加载到WebView上以启动身份验证过程。

TIP: You will probably need to enable JavaScript on your WebView to access the OAuth provider website.

提示:您可能需要在WebView上启用JavaScript才能访问OAuth提供程序网站。

处理身份验证重定向 (Handling the Authentication Redirect)

Once the user completes the authentication successfully, the WebView will be redirected to the previously specified URL including the authorization code as a parameter.

用户成功完成身份验证后,WebView将被重定向到先前指定的URL,包括授权码作为参数。

Example OAuth2 Authorization Code Response
OAuth2授权码响应示例

To obtain the code from the redirected URL, set the WebViewClient to intercept the redirect and parse the URL.

要从重定向的URL获取code ,请将WebViewClient设置为拦截重定向并解析URL。

WebViewClient listening for the Authorization response
WebViewClient监听授权响应

Now that you have the authorization code, you can send it to your server to obtain the tokens for you (this is the most secure option) or continue reading through Part 2 to see how to handle that from the App.

现在您已经拥有了授权码,您可以将其发送到服务器以为您获取令牌(这是最安全的选择),或者继续阅读第2部分,以了解如何从应用程序中处理令牌。

额外:隐式代码流 (Extra: Implicit Code Flow)

The above steps can be easily changed to authenticate with the OAuth2 Implicit Code Flow. Just build the request with response_type: token to obtain an access_token instead of a code in the redirect.

可以轻松更改上述步骤,以使用OAuth2隐式代码流进行身份验证。 只需使用response_type: token构建请求即可获得一个access_token而不是重定向中的code

This access token will grant you access to the provider resources, however, once it expires, the user will have to go through the login process again to generate a new one.

该访问令牌将授予您访问提供者资源的权限,但是,一旦过期,用户将不得不再次执行登录过程以生成新的。

第2部分。获取令牌 (Part 2. Getting the Tokens)

Disclaimer: This part is better suited to be handled in your server in order to avoid storing the client secret in the app. As an alternative, OAuth Providers can also implement the Authorization Code Flow with PKCE to prevent attackers from exchanging the compromised authorization code for access tokens.

免责声明:此部分更适合在服务器中处理,以避免在应用程序中存储客户端密码。 作为替代方案,OAuth提供商也可以 使用PKCE 实施授权代码流 以防止攻击者将受损的授权代码交换为访问令牌。

Use ktor (or any other networking library) to execute the request to exchange the authorization code for the access token and refresh token. To learn how to set up and use Ktor check out this post.

使用ktor(或任何其他网络库)执行请求以交换访问令牌和刷新令牌的授权代码。 要了解如何设置和使用Ktor, 请查看这篇文章

Send a POST request to the /token endpoint available in your OAuth provider with the following parameters:

使用以下参数将POST请求发送到OAuth提供程序中的/token端点:

Client ID, Client Secret, Authorization Code (from Step 1), Grant Type (“authorization_code”), and the Redirect URL (same as before).

Client IDClient SecretAuthorization Code ( 来自步骤1 ), Grant Type ( “ authorization_code” )和Redirect URL ( 与之前相同 )。

OAuth2 Tokens Request with Ktor
向Ktor请求OAuth2令牌

The response can be deserialized into the following data class:

可以将响应反序列化为以下数据类:

Token Response Model
令牌响应模型

Finally, save both the access token and refresh token locally.

最后,在本地保存访问令牌和刷新令牌。

From now on, to authenticate the user in every API request, simply add the following authorization header to every request:Authorization: Bearer accessToken

从现在开始,要在每个API请求中对用户进行身份验证,只需将以下授权标头添加到每个请求中: Authorization: Bearer accessToken

With the Authorization header added, you can now try and execute any authenticated request to your API.

添加了Authorization标头后,您现在可以尝试执行对API的任何身份验证请求。

However, depending on the OAuth Provider settings, the access token will eventually expire. Check the next part for how to obtain a new access token.

但是,根据OAuth Provider的设置,访问令牌最终会过期 。 检查下一部分,了解如何获取新的访问令牌。

第3部分。刷新访问令牌 (Part 3. Refreshing the Access Token)

The next request you execute with an expired access token will return an Unauthorized error with status code 401 .

您使用过期的访问令牌执行的下一个请求将返回Unauthorized 错误 状态码为401

When that happens, you must open the WebView page again, to restart the authentication flow from the beginning and obtain a new access token.

发生这种情况时,您必须再次打开WebView页面,以从头开始重新启动身份验证流程并获取新的访问令牌

However, if you have a refresh token from the previous step, you can use it to automatically request a new access token and skip repeating the authentication process. Note that not all providers support this and instead require user interaction to get a new access token.

但是,如果您具有上一步中的刷新令牌 ,则可以使用它来自动请求新的访问令牌,并跳过重复身份验证过程的步骤。 请注意,并非所有提供程序都支持此功能,而是需要用户交互才能获取新的访问令牌。

使用刷新令牌请求新的访问令牌 (Requesting a new access token with the refresh token)

Once the token expires, simply execute a new request to the same /token endpoint used to get the access code the first time. In this request, however, set the grant_type to refresh_token, skip the redirect_uri, and send the refresh_token instead of the initial code.

令牌过期后,只需对首次获取访问代码所用的同一/token端点执行一个新请求即可。 但是,在此请求中,将grant_type设置为refresh_token ,跳过redirect_uri ,然后发送refresh_token而不是初始代码。

OAuth2 Refresh Token Request with Ktor
使用Ktor的OAuth2刷新令牌请求

The response contains a new valid access token and a new refresh token to use in the next expiration.

该响应包含一个新的有效访问令牌和一个新的刷新令牌,以在下一次到期时使用。

Store them safely and execute the errored request again.

安全地存储它们,然后再次执行错误的请求。

额外:拦截未经授权的401 (Extra: Intercepting the 401 Unauthorized)

To manage the above refresh process, you can use an Interceptor (with OkHttp) or a Feature (with ktor). Its task is going to be:

要管理上述刷新过程,可以使用拦截器 (带有OkHttp)或功能(带有ktor)。 它的任务将是:

  1. On every HTTP response, check the status code.

    在每个HTTP响应上,检查状态代码。
  2. If the value is 401 execute the “refresh token” request.

    如果值为401执行“刷新令牌”请求。

  3. Retry the request with the new tokens.

    使用新令牌重试请求。

Note that you still need to handle the case where the refresh request did not work, and a second 401 error is received. In that situation, redirect the user to perform the authentication flow again.

请注意,您仍然需要处理刷新请求不起作用,并且收到第二个401错误的情况。 在这种情况下,请重定向用户以再次执行身份验证流程。

For ktor, you can use this great OAuthFeature on GitHub (credits to Kurt Renzo) to perform the steps described above.

对于ktor,您可以在GitHub上使用此出色的OAuthFeature (归功于Kurt Renzo )来执行上述步骤。

Add the Feature class to your project and follow the Usage instructions from the repository.

将Feature类添加到您的项目中,并按照存储库中的用法说明进行操作

附加:安全最佳实践 (Extra: Security Best Practices)

  • Logs: For the purpose of this article, tokens are being printed in the LogCat console. Ensure no tokens or keys ever go through the console in production builds as those can be easily debugged and obtained.

    日志 :就本文而言,令牌是在LogCat控制台中打印的。 确保生产环境中的令牌或密钥不会通过控制台,因为可以轻松地调试和获取令牌。

  • Storing Tokens: To store and retrieve tokens, use EncryptedSharedPreferences instead of the regular SharedPreferences to prevent saving them in plain text.

    存储令牌 :要存储和检索令牌,请使用EncryptedSharedPreferences而不是常规的SharedPreferences来防止将其保存为纯文本。

  • Storing API Keys and Secrets: To prevent your API keys from being obtained through reverse-engineering the app’s apk file, store them in a C/C++native class to make them much harder to find.

    存储API密钥和机密 :为了防止通过反向工程应用程序的apk文件获得API密钥,请将它们存储在C / C ++ native类中,以使其更难找到。

You can read more around Security Practices on this great post by ProAndroidDev.com

您可以在ProAndroidDev.com的这篇精彩文章中阅读有关安全实践的更多信息

At L+R we are always looking for passionate developers to join our team. Check out our openings on the careers page.

L + R,我们一直在寻找热情的开发人员加入我们的团队。 在职业页面上查看我们的职位空缺。

翻译自: https://medium.com/l-r-engineering/oauth2-in-android-authorization-code-flow-ffc4355dd473

 类似资料: