从0开始构建一个Oauth2Server服务 <4> 构建服务器端应用程序

构建服务器端应用程序

以下分步示例说明了将授权代码流与 PKCE 结合使用。

开始

高级概述是这样的:

  • 使用应用程序的客户端 ID、重定向 URL、状态和 PKCE 代码质询参数创建登录链接
  • 用户看到授权提示并批准请求
  • 使用授权码将用户重定向回应用程序的服务器
  • 该应用程序交换访问令牌的授权代码
App发起授权请求

该应用程序通过制作包含客户端 ID、范围、状态和 PKCE 代码验证程序的 URL 来启动流程。该应用程序可以将其放入<a href="">标签中。

代码语言:javascript
复制
<a href="https://authorization-server.com/oauth/authorize
?response_type=code&client_id=mRkZGFjM&state=5ca75bd30
&scope=photos
&code_challenge_method=S256
&code_challenge=hKpKupTM391pE10xfQiorMxXarRKAHRhTfH_xkGf7U4">
Connect Your Account</a>

请注意,这不是您的应用程序正在进行的 HTTP 调用,而是用户单击以将其浏览器重定向到 OAuth 服务器的 URL。

用户批准请求

在被定向到授权服务器后,用户会看到如下图所示的授权请求。如果用户批准请求,他们将连同授权码和状态参数一起被重定向回应用程序。

从0开始构建一个Oauth2Server服务 <4> 构建服务器端应用程序 _服务器

示例授权请求

该服务将用户重定向回应用程序

该服务发送一个重定向标头,将用户的浏览器重定向回发出请求的应用程序。重定向将在 URL 中包含一个“代码”和原始“状态”。

代码语言:javascript
复制
https://example-app.com/cb?code=Yzk5ZDczMzRlNDEwY&state=5ca75bd30

(这实际上将作为 HTTP 响应从授权服务器发送回用户的浏览器,而不是您的应用程序。此处未显示实际的 HTTP 响应,因为它对您在应用程序中编写的代码并不重要。)

该应用程序交换访问令牌的授权代码

最后,应用程序使用授权代码通过向授权服务器的令牌端点发出 HTTPS POST 请求来获取访问令牌。

代码语言:javascript
复制
POST /oauth/token HTTP/1.1
Host: authorization-server.com

code=Yzk5ZDczMzRlNDEwY
&grant_type=code
&redirect_uri=https://example-app.com/cb
&client_id=mRkZGFjM
&client_secret=ZGVmMjMz
&code_verifier=Th7UHJdLswIYQxwSg29DbK1a_d9o41uNMTRmuH0PM8zyoMAQ

授权服务器验证请求并使用访问令牌和可选的刷新令牌进行响应(如果访问令牌将过期)。

服务响应:

代码语言:javascript
复制
{
"access_token": "AYjcyMzY3ZDhiNmJkNTY",
"refresh_token": "RjY2NjM5NzA2OWJjuE7c",
"token_type": "Bearer",
"expires": 3600
}
可能的错误

在几种情况下,您可能会在授权期间收到错误响应。

通过在查询字符串中使用附加参数重定向回提供的重定向 URL 来指示错误。总会有一个错误参数,重定向也可能包括error_descriptionerror_uri

代码语言:javascript
复制

尽管服务器返回一个error_description密钥,但错误描述并不打算显示给用户。相反,您应该向用户显示您自己的错误消息。这使您可以告诉用户采取适当的措施来纠正问题,如果您正在构建多语言网站,还可以让您有机会本地化错误消息。

重定向网址无效

如果提供的重定向 URL 无效,授权服务器将不会重定向到它。相反,它可能会向用户显示一条描述问题的消息。

无法识别client_id

如果无法识别客户端 ID,授权服务器将不会重定向用户。相反,它可能会显示一条描述问题的消息。

用户拒绝请求

如果用户拒绝授权请求,服务器会将用户重定向回error=access_denied查询字符串中的重定向 URL,并且不会出现任何代码。此时由应用程序决定向用户显示什么。

参数无效

如果一个或多个参数无效,例如缺少所需的值或参数response_type错误,服务器将重定向到重定向 URL 并包括描述问题的查询字符串参数。error 参数的其他可能值是:

invalid_request: 请求缺少必需的参数,包括无效的参数值,或者格式不正确。

unauthorized_client: 客户端无权使用此方法请求授权码。

unsupported_response_type: 授权服务器不支持通过该方式获取授权码。

invalid_scope: 请求的范围无效、未知或格式错误。

server_error: 授权服务器遇到意外情况,无法满足请求。

temporarily_unavailable: 由于服务器临时过载或维护,授权服务器当前无法处理请求。

此外,服务器可能包括参数error_descriptionerror_uri有关错误的附加信息。

用户体验与注意事项

为了确保授权码授予的安全,授权页面必须出现在用户熟悉的 Web 浏览器中,不得嵌入 iframe 弹出窗口或移动应用程序的嵌入式浏览器中。如果它可以嵌入到另一个网站中,用户将无法验证它是合法服务而不是网络钓鱼尝试。

如果应用程序想要使用授权码授予但不能保护其秘密(即本机移动应用程序或单页 JavaScript 应用程序),则在发出请求以交换授权码以获取访问令牌时不需要客户端秘密,并且还必须使用 PKCE。但是,某些服务仍然不支持 PKCE,因此可能无法从单页应用程序本身执行授权流程,并且客户端 JavaScript 代码可能需要具有执行 OAuth 的配套服务器端组件流动代替。