Epic游戏服务(Epic Games Services)使用OAuth 2.0协议进行身份验证和授权,它支持常见的web服务器和客户端应用用例。此外,Epic还针对一些特殊用例引入了自定义授权类型。
在开始之前,你需要从Epic获取OAuth 2.0客户端凭证。凭证会以客户端ID和密钥的形式提供,你从Epic授权服务器请求访问令牌时会用到它们。更多详情请参阅Epic账号服务入门中的使用指南。
场景
Epic Games商店中的游戏客户端
从 Epic Games商店 启动的游戏客户端会使用一次性 交换码 通过Epic授权服务器进行身份验证。该交换码由 Epic启动程序 生成,作为命令行参数传递到 游戏客户端。
游戏客户端启动后,会向Epic令牌端点发出请求,请求包中的信息包括客户端凭证和交换代码。响应包的内容包括 访问令牌、刷新令牌(可选) 以及完成身份验证的客户端和账号的相关信息。
游戏客户端随后对Epic服务发出的所有请求都将包含该访问令牌。访问令牌还可传递至受信任的游戏服务器用于验证,或者用于服务到服务请求。
适用情况下,令牌响应中还可包含刷新令牌。访问令牌到期(一般为2小时)后,游戏客户端将需要使用刷新令牌。这通常会在访问令牌签发后的2小时内发生。
展示Epic Games启动程序身份验证流程的图表。点击查看大图。
Web服务器应用程序
对于服务器端应用程序,该流程首先引导用户在Web浏览器上完成Epic授权流程来获取 授权代码。系统将要求用户使用其Epic Games账号登录,并可能要求用户授权应用程序。授权完成后,用户代理将以授权代码作为查询参数,重定向回你的Web应用程序。
重定向之后,Web服务器将向Epic令牌端点发出请求,包括客户端凭证和授权代码。响应将包括访问令牌以及完成身份验证的客户端和账号的相关信息。
Web服务器随后对Epic服务发出的所有请求都将包含该访问令牌。
身份验证
作为验证流程中的第一步,用户必须使用Epic服务进行身份验证。我们仅支持使用OAuth 2.0进行身份验证,以及自定义授权类型。
当客户端获得了请求令牌所需的信息(包括交换代码、授权代码、用户凭证等)后,它会首先请求获取访问令牌。
请求访问令牌
要请求访问令牌,客户端将向Epic令牌端点发出HTTP请求,同时传递客户端凭证(客户端ID和密钥)和用户凭证。
Epic令牌端点地址为https://api.epicgames.dev/epic/oauth/v1/token。
客户端凭证将使用基本授权模式在Authorization标头(header)中传递。此端点支持下列post参数:
参数名称 |
说明 |
|
---|---|---|
|
使用的授权类型: |
|
|
客户端试图用于身份验证的部署ID。此参数会影响与其他需要部署的服务的交互。如果不是公共部署,则只有授权用户才能登录。 |
|
|
用户请求的范围(权限)列表,以空格分隔。例如: |
|
对于 |
||
|
账号用户名(电子邮件地址),仅用于 |
|
|
账号密码,仅用于密码授权类型。 |
|
对于 |
||
|
启动器传递到游戏客户端的交换代码,仅用于exchange_code授权类型。 |
|
对于 |
||
|
从授权服务器中获取的授权代码。 |
|
对于 |
||
|
应用程序的OAuth客户端ID。 |
|
|
应用程序的OAuth客户端密钥。 |
这些参数应包含在请求体中。查询参数将被忽略。
以下片段展示了一段简单的请求示例(使用 密码
授权类型):
POST /epic/oauth/v1/token HTTP/1.1
Host: api.epicgames.dev
Content-Type: application/x-www-form-urlencoded
Authorization: Basic Zm9vOmJhcg==
grant_type=password&
deployment_id=foo&
scope=basic_profile friends_list presence&
username=user@example.com&
password=s3cr3t
响应包含以下字段:
响应 |
说明 |
---|---|
|
访问令牌,可以有一个前缀(例如: |
|
令牌过期之前的秒数。 |
|
ISO 8601格式的到期日期。 |
|
令牌目标用户的Epic账号ID。 |
|
用于生成此令牌的客户端ID。 |
|
与客户端关联的应用程序ID。 |
|
令牌类型,该值将始终为 |
|
刷新令牌,根据客户端配置返回的可选项。此刷新令牌可用于在访问令牌到期之前或之后延长会话。 |
|
刷新令牌到期前的秒数。 |
|
ISO 8601格式的刷新令牌到期时间。 |
以下片段展示了一段简单的响应示例:
{
"scope": "basic_profile friends_list presence",
"token_type": "bearer",
"access_token": "eyJ0IjoiZXBpY19pZCIsImFsZyI6IlJTMjU2Iiwia2lkIjoibldVQzlxSFVldWRHcnBXb3FvVXVHZkFIYmVWM2NsRnlsdFRYMzhFbXJKSSJ9.eyJhdWQiOiJ4eXphNzg5MWxoeE1WWUdDT043TGduS1paOEhRR0Q1SCIsInN1YiI6Ijk2MjZmNDQxMDU1MzQ5Y2U4Y2I3ZDdkNWE0ODNlYWEyIiwidCI6ImVwaWNfaWQiLCJzY29wZSI6ImJhc2ljX3Byb2ZpbGUgZnJpZW5kc19saXN0IHByZXNlbmNlIiwiYXBwaWQiOiJmZ2hpNDU2N08wM0hST3hFandibjdrZ1hwQmhuaFd3diIsImlzcyI6Imh0dHBzOlwvXC9hcGkuZXBpY2dhbWVzLmRldlwvZXBpY1wvb2F1dGhcL3YxIiwiZG4iOiJLcm5icnkiLCJleHAiOjE1ODgyODYwODMsImlhdCI6MTU4ODI3ODg4Mywibm9uY2UiOiJuLUI1cGNsSXZaSkJaQU1KTDVsNkdvUnJDTzNiRT0iLCJqdGkiOiI2NGMzMGQwMjk4YTM0MzdjOGE3NGU1OTAxYzM0ODZiNSJ9.MZRoCRpjIb--dD7hxoo2GvjSPhUSNpOq1FhtShTBmzMJ1qlHFPzNaUiAEETAc3mabGPKyOxUP6Q1FBadr_P_UtbtB7kf34hN2VTv5czW6WOx1HdpjwUQZuxFyDc_aix7FCS0Egu4rZlC65b-B0FUVlial_s_FrH8ou5L_d-4I0KVpIwtv-b_M6EQ9jtLdQRfMaP6aV0rIerrbqFZ617Pe7XT4IO9jZFwM8F5aDTeDHkkOO41wyVibrm38799lP4B65RIv9CwbAL-TVmV1L5gFYITaZhi5ShfZzTvxAk-3Dxwp8c5JvcO68zpbya5gFSAfhsd7vt9YLU0gQR2uXq3Vw",
"refresh_token": "eyJ0IjoiZXBpY19pZCIsImFsZyI6IlJTMjU2Iiwia2lkIjoibldVQzlxSFVldWRHcnBXb3FvVXVHZkFIYmVWM2NsRnlsdFRYMzhFbXJKSSJ9.eyJhdWQiOiJ4eXphNzg5MWxoeE1WWUdDT043TGduS1paOEhRR0Q1SCIsInN1YiI6Ijk2MjZmNDQxMDU1MzQ5Y2U4Y2I3ZDdkNWE0ODNlYWEyIiwidCI6ImVwaWNfaWQiLCJhcHBpZCI6ImZnaGk0NTY3TzAzSFJPeEVqd2JuN2tnWHBCaG5oV3d2Iiwic2NvcGUiOiJiYXNpY19wcm9maWxlIGZyaWVuZHNfbGlzdCBwcmVzZW5jZSIsImlzcyI6Imh0dHBzOlwvXC9hcGkuZXBpY2dhbWVzLmRldlwvZXBpY1wvb2F1dGhcL3YxIiwiZG4iOiJLcm5icnkiLCJleHAiOjE1ODgzMDc2ODMsImlhdCI6MTU4ODI3ODg4MywianRpIjoiYzczYjA2NmUyZDU4NGVkNTk0NjZiOThiNzI3NzJiMjAifQ.O-eVa46NimubKwxe9SwlHxciivu0XWe1-DSL74mMiA_PpPoW0yKL9DfmsLxiPCwsRB5_hQTc6_FM7G1FyfKtX_VVAp90MZPkhCbAbfKmTpQVcL0Ya6kve4KMG8KxeLVfLLhubCbJTYlnDNVHobbpvpQtHd8Ys321ZNDJj05l_tnZzdgus-xmCO6orX4UP4wDd1jAOXXeqRT47OXuLCgSE0q6Osfh-ENPwh6ph1i7ld759xPV0oNcQb8XiPxnT6_FUmFugzG1YS1z9bTnVWmbP2RmYluue5VQm5EKGJZ91Alve8s2eNEtDfUqaBLZ45pqGkc1KjbYTtP0a_1ue2BpkQ",
"expires_in": 7200,
"expires_at": "2020-04-30T22:34:43.549Z",
"refresh_expires_in": 28800,
"refresh_expires_at": "2020-05-01T04:34:43.549Z",
"account_id": "9626f441055349ce8cb7d7d5a483eaa2",
"client_id": "xyza7891lhxMVYGCON7LgnKZZ8HQGD5H",
"application_id": "fghi4567O03HROxEjwbn7kgXpBhnhWwv"
}
访问令牌应始终按 Authorization
头文件中的原样传递给Epic服务。例如:Authorization: Bearer eyJraWQiOiJ0RkM...
Web应用程序
对于Web应用程序和服务器端应用程序,在请求访问令牌之前需要获得授权代码。使用Web授权之前,你的Epic OAuth客户端必须配置一个重定向URL。
要启动该流程,应用程序需将用户重定向到授权页面,该页面将要求用户登录Epic Games账号。授权URL为:
https://www.epicgames.com/id/authorize?client_id={client_id}&response_type=code&scope=basic_profile
你可以使用额外参数 redirect_uri={redirect_uri}
来定义多个重定向URL。如果多于一条重定向,验证URL是:
https://www.epicgames.com/id/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code&scope=basic_profile
用户登录后,将使用授权代码返回到已配置的重定向URL。你的应用程序应该使用该代码获取访问令牌,并使用请求访问令牌中所述的 authorization_code
授权类型。
我们还支持可选的状态参数,该参数可维持请求与回调之间的状态,用于防止跨站点请求伪造攻击。
以下是用户身份验证后的重定向示例:
https://www.example.com/?code=cfd1de1a8d224203b0445fe977838d81&state=rfGWJux2WL86Zxr6nKApCAnDo8KexEUE
验证访问令牌
离线验证
访问令牌是一个 JWT (JSON 网络令牌),其中包括用于验证令牌真实性的标头和签名。
验证签名前,需要从我们的服务中获取以JSON Web密钥(JWK)形式发布的 公共密钥。我们使用JWK集合格式(包括当前所有公共密钥)公开单个端点。虽然密钥集不会频繁更改,但随时可能轮换使用密钥。
JWK端点地址为 https://api.epicgames.dev/epic/oauth/v1/.well-known/jwks.json
建议已获取公共密钥的客户端将密钥缓存一段合理时间。虽然密钥会轮换使用,但对于给定的密钥ID,其密钥不会更改。
以下片段展示了从公共密钥端点返回的响应示例:
{"keys":[{"kty":"RSA","e":"AQAB","kid":"WMS7EnkIGpcH9DGZsv2WcY9xsuFnZCtxZjj4Ahb-_8E","n":"l6XI48ujknQQlsJgpGXg4l2i_DuUxuG2GXTzkOG7UtX4MqkVBCfW1t1JIIc8q0kCInC2oBwhC599ZCmd-cOi0kS7Aquv68fjERIRK9oCUnF_lJg296jV8xcalFY0FOWX--qX3xGKL33VjJBMIrIu7ETjj06s-v4li22CnHmu2lDkrp_FPTVzFscn-XRIojqIFb7pKRFPt27m12FNE_Rd9bqlVCkvMNuE7VTpTOrSfKk5B01M5IuXKXk0pTAWnelqaD9bHjAExe2I_183lp_uFhNN4hLTjOojxl-dK8Jy2OCPEAsg5rs9Lwttp3zZ--y0sM7UttN2dE0w3F2f352MNQ"}]}
在线验证
如果无法选择离线验证,可以使用有效的访问令牌来调用令牌信息端点,进行在线验证。此端点实现了令牌内省规范。
此令牌信息端点地址为 https://api.epicgames.dev/epic/oauth/v1/tokenInfo
下列片段展示了用于验证令牌的请求示例:
POST /epic/oauth/v1/tokenInfo HTTP/1.1
Host: api.epicgames.dev
Content-Type: application/x-www-form-urlencoded
token=eyJ0IjoiZXBpY19pZCIsImFsZyI6IlJTMjU2Iiwia2lkIjoibldVQzlxSFVldWRHcnBXb3FvVXVHZkFIYmVWM2NsRnlsdFRYMzhFbXJKSSJ9.eyJhdWQiOiJ4eXphNzg5MWxoeE1WWUdDT043TGduS1paOEhRR0Q1SCIsInN1YiI6Ijk2MjZmNDQxMDU1MzQ5Y2U4Y2I3ZDdkNWE0ODNlYWEyIiwidCI6ImVwaWNfaWQiLCJzY29wZSI6ImJhc2ljX3Byb2ZpbGUgZnJpZW5kc19saXN0IHByZXNlbmNlIiwiYXBwaWQiOiJmZ2hpNDU2N08wM0hST3hFandibjdrZ1hwQmhuaFd3diIsImlzcyI6Imh0dHBzOlwvXC9hcGkuZXBpY2dhbWVzLmRldlwvZXBpY1wvb2F1dGhcL3YxIiwiZG4iOiJLcm5icnkiLCJleHAiOjE1ODgyODYwODMsImlhdCI6MTU4ODI3ODg4Mywibm9uY2UiOiJuLUI1cGNsSXZaSkJaQU1KTDVsNkdvUnJDTzNiRT0iLCJqdGkiOiI2NGMzMGQwMjk4YTM0MzdjOGE3NGU1OTAxYzM0ODZiNSJ9.MZRoCRpjIb--dD7hxoo2GvjSPhUSNpOq1FhtShTBmzMJ1qlHFPzNaUiAEETAc3mabGPKyOxUP6Q1FBadr_P_UtbtB7kf34hN2VTv5czW6WOx1HdpjwUQZuxFyDc_aix7FCS0Egu4rZlC65b-B0FUVlial_s_FrH8ou5L_d-4I0KVpIwtv-b_M6EQ9jtLdQRfMaP6aV0rIerrbqFZ617Pe7XT4IO9jZFwM8F5aDTeDHkkOO41wyVibrm38799lP4B65RIv9CwbAL-TVmV1L5gFYITaZhi5ShfZzTvxAk-3Dxwp8c5JvcO68zpbya5gFSAfhsd7vt9YLU0gQR2uXq3Vw
以下片段展示了令牌信息端点的响应示例:
{
"active": true,
"scope": "basic_profile friends_list presence",
"token_type": "bearer",
"expires_in": 6761,
"expires_at": "2020-04-30T22:34:43.549Z",
"account_id": "9626f441055349ce8cb7d7d5a483eaa2",
"client_id": "xyza7891lhxMVYGCON7LgnKZZ8HQGD5H",
"application_id": "fghi4567O03HROxEjwbn7kgXpBhnhWwv"
}
访问令牌和刷新令牌都可以使用此端点进行验证。
Epic Games商店中的游戏客户端
当你使用某个通过Epic Games商城连接的游戏客户端时,游戏客户端必须使用 exchange_code
授权类型来获取访问令牌,并传递其客户端凭证以及从启动器传递而来的代码。
在开发期间,你还可以使用 密码
授权类型,以便游戏客户端根据Epic服务进行身份验证,从而无需集成启动器。
当Epic Games启动程序启动游戏时,将传递以下命令行参数:
参数 |
说明 |
---|---|
|
不用于交换代码。 |
|
游戏客户端将使用的凭证。其中包括要传递到身份验证服务器的交换代码。 |
|
内部应用程序名称。 |
|
启动时环境(始终是 |
|
在启动器中经过身份验证的账号的显示名称。 |
|
在启动器中经过身份验证的账号的Epic账号ID。 |
|
基于用户偏好或系统语言的首选区域设置。 |
|
包含所有权验证令牌信息的文件的完整路径。 |
以下是启动器传递命令行参数的示例:
-AUTH_LOGIN=unused -AUTH_PASSWORD=ed642dfd4e6f47bf8354caf1bcab2fc2 -AUTH_TYPE=exchangecode -epicapp=[AppName] -epicenv=Prod -epicusername="DisplayName" -epicuserid=ab1f86d911d74f4aa8399849e9ca9aa5 -epiclocale=en-US
-epicovt="C:/AppName/.egstore/ab1f86d911d74f4aa8399849e9ca9aa5/File.ovt"
账号信息
一旦拥有了访问令牌,你就能够代表用户向Epic服务发出请求。例如,你可以请求获取显示名称,或者用户好友的显示名称。
从访问令牌
你可以直接从访问令牌获取用户的一些相关信息。令牌被编码为 JSON Web令牌(JWT) 格式,它的头文件包含令牌类型和签名信息、一个有效负载(其中包含用户、客户端和会话的相关信息)以及一个签名。我们建议使用库来进行JWT的解码操作,如果无法使用库,请参考JWT规范。
该有效负载将包括以下信息:
声明 |
类型 |
说明 |
---|---|---|
iss |
字符串 |
发布令牌的Epic Games身份验证服务器的基本URI。 |
sub |
字符串 |
使用client_credentials授权类型时,不会出现此声明。 |
aud |
字符串 |
授权请求中指定的客户端ID。 |
iat |
整型 |
令牌发布时间,UNIX时间戳格式。 |
exp |
整型 |
令牌过期时间,UNIX时间戳格式。 |
jti |
字符串 |
此令牌的唯一标识符。 |
t |
字符串 |
令牌类型。此类型应始终为epic_id。该字符串将替代EG1令牌使用的版本前缀。 |
scope |
字符串 |
授权给用户的范围列表,以空格分隔。 |
dn |
字符串 |
用户的显示名称。 |
appid |
字符串 |
授权请求中指定的应用程序ID。 |
pfpid |
字符串 |
令牌请求中指定的用于部署的产品ID。 |
pfsid |
字符串 |
令牌请求中指定的用于部署的沙盒ID。 |
pfdid |
字符串 |
令牌请求中指定的部署ID。 |
我们可以使用前面例子中的令牌对有效负载进行解码,检查其是否包含以下内容:
{
"aud": "xyza7891lhxMVYGCON7LgnKZZ8HQGD5H",
"sub": "9626f441055349ce8cb7d7d5a483eaa2",
"t": "epic_id",
"scope": "basic_profile friends_list presence",
"appid": "fghi4567O03HROxEjwbn7kgXpBhnhWwv",
"iss": "https://api.epicgames.dev/epic/oauth/v1",
"dn": "Krnbry",
"exp": 1588286083,
"iat": 1588278883,
"nonce": "n-B5pclIvZJBZAMJL5l6GoRrCO3bE=",
"jti": "64c30d0298a3437c8a74e5901c3486b5"
}
获取账号
除了访问令牌中的信息之外,还可以请求已登录账号的类似信息,或任何与你的应用程序交互的其他账号的类似信息。
出于隐私方面的考虑,你只能请求之前已同意使用你的应用程序的那些用户的账号信息。
账号信息端点地址为 https://api.epicgames.dev/epic/id/v1/accounts
。调用此端点时,你需要为正在尝试解析的账号指定一个或多个accountId查询参数。单个请求最多允许包含 50 个账号。
下列片段展示了获取多个账号信息的请求示例:
GET /epic/id/v1/accounts?accountId=foo531f86d911d74f4aa8399849e9ca9ba6&accountId=barcbab941052f540c69fbd92ddc3bf9027 HTTP/1.1
Host: api.epicgames.dev
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer [token]eyJ0IjoiZXBpY19pZCIsImFsZyI6IlJTMjU2Iiwia2lkIjoibldVQzlxSFVldWRHcnBXb3FvVXVHZkFIYmVWM2NsRnlsdFRYMzhFbXJKSSJ9.eyJhdWQiOiJ4eXphNzg5MWxoeE1WWUdDT043TGduS1paOEhRR0Q1SCIsInN1YiI6Ijk2MjZmNDQxMDU1MzQ5Y2U4Y2I3ZDdkNWE0ODNlYWEyIiwidCI6ImVwaWNfaWQiLCJzY29wZSI6ImJhc2ljX3Byb2ZpbGUgZnJpZW5kc19saXN0IHByZXNlbmNlIiwiYXBwaWQiOiJmZ2hpNDU2N08wM0hST3hFandibjdrZ1hwQmhuaFd3diIsImlzcyI6Imh0dHBzOlwvXC9hcGkuZXBpY2dhbWVzLmRldlwvZXBpY1wvb2F1dGhcL3YxIiwiZG4iOiJLcm5icnkiLCJleHAiOjE1ODgyODYwODMsImlhdCI6MTU4ODI3ODg4Mywibm9uY2UiOiJuLUI1cGNsSXZaSkJaQU1KTDVsNkdvUnJDTzNiRT0iLCJqdGkiOiI2NGMzMGQwMjk4YTM0MzdjOGE3NGU1OTAxYzM0ODZiNSJ9.MZRoCRpjIb--dD7hxoo2GvjSPhUSNpOq1FhtShTBmzMJ1qlHFPzNaUiAEETAc3mabGPKyOxUP6Q1FBadr_P_UtbtB7kf34hN2VTv5czW6WOx1HdpjwUQZuxFyDc_aix7FCS0Egu4rZlC65b-B0FUVlial_s_FrH8ou5L_d-4I0KVpIwtv-b_M6EQ9jtLdQRfMaP6aV0rIerrbqFZ617Pe7XT4IO9jZFwM8F5aDTeDHkkOO41wyVibrm38799lP4B65RIv9CwbAL-TVmV1L5gFYITaZhi5ShfZzTvxAk-3Dxwp8c5JvcO68zpbya5gFSAfhsd7vt9YLU0gQR2uXq3Vw
以下片段是上述请求的响应示例:
[
{
"accountId": "531f86d911d74f4aa8399849e9ca9ba6",
"displayName": "eas_user",
"preferredLanguage": "en",
"linkedAccounts": [
{
"identityProviderId": "xbl",
"displayName": "eas_user_xbl"
}
]
},
{
"accountId": "cbab941052f540c69fbd92ddc3bf9027",
"displayName": "other_user",
"preferredLanguage": "en"
}
]