fix(game): issue compact AMG provider token
This commit is contained in:
parent
290e9ceb93
commit
aa0ddbc3b0
@ -1193,6 +1193,8 @@ type LaunchGameRequest struct {
|
|||||||
AccessToken string `protobuf:"bytes,7,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
AccessToken string `protobuf:"bytes,7,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
|
||||||
// country_id 由 gateway 从当前用户资料注入,供 provider 回调订单按国家统计。
|
// country_id 由 gateway 从当前用户资料注入,供 provider 回调订单按国家统计。
|
||||||
CountryId int64 `protobuf:"varint,8,opt,name=country_id,json=countryId,proto3" json:"country_id,omitempty"`
|
CountryId int64 `protobuf:"varint,8,opt,name=country_id,json=countryId,proto3" json:"country_id,omitempty"`
|
||||||
|
// provider_token 是 gateway 签发的短期厂商令牌;仅需要限长令牌的 adapter 使用。
|
||||||
|
ProviderToken string `protobuf:"bytes,9,opt,name=provider_token,json=providerToken,proto3" json:"provider_token,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@ -1283,6 +1285,13 @@ func (x *LaunchGameRequest) GetCountryId() int64 {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *LaunchGameRequest) GetProviderToken() string {
|
||||||
|
if x != nil {
|
||||||
|
return x.ProviderToken
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
type LaunchGameResponse struct {
|
type LaunchGameResponse struct {
|
||||||
state protoimpl.MessageState `protogen:"open.v1"`
|
state protoimpl.MessageState `protogen:"open.v1"`
|
||||||
SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"`
|
SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"`
|
||||||
@ -6949,7 +6958,7 @@ const file_proto_game_v1_game_proto_rawDesc = "" +
|
|||||||
"\x14bridge_script_sha256\x18\x03 \x01(\tR\x12bridgeScriptSha256\"~\n" +
|
"\x14bridge_script_sha256\x18\x03 \x01(\tR\x12bridgeScriptSha256\"~\n" +
|
||||||
"\x17GetBridgeScriptResponse\x12=\n" +
|
"\x17GetBridgeScriptResponse\x12=\n" +
|
||||||
"\x06config\x18\x01 \x01(\v2%.hyapp.game.v1.GameBridgeScriptConfigR\x06config\x12$\n" +
|
"\x06config\x18\x01 \x01(\v2%.hyapp.game.v1.GameBridgeScriptConfigR\x06config\x12$\n" +
|
||||||
"\x0eserver_time_ms\x18\x02 \x01(\x03R\fserverTimeMs\"\x8e\x02\n" +
|
"\x0eserver_time_ms\x18\x02 \x01(\x03R\fserverTimeMs\"\xb5\x02\n" +
|
||||||
"\x11LaunchGameRequest\x12.\n" +
|
"\x11LaunchGameRequest\x12.\n" +
|
||||||
"\x04meta\x18\x01 \x01(\v2\x1a.hyapp.game.v1.RequestMetaR\x04meta\x12\x17\n" +
|
"\x04meta\x18\x01 \x01(\v2\x1a.hyapp.game.v1.RequestMetaR\x04meta\x12\x17\n" +
|
||||||
"\auser_id\x18\x02 \x01(\x03R\x06userId\x12&\n" +
|
"\auser_id\x18\x02 \x01(\x03R\x06userId\x12&\n" +
|
||||||
@ -6959,7 +6968,8 @@ const file_proto_game_v1_game_proto_rawDesc = "" +
|
|||||||
"\aroom_id\x18\x06 \x01(\tR\x06roomId\x12!\n" +
|
"\aroom_id\x18\x06 \x01(\tR\x06roomId\x12!\n" +
|
||||||
"\faccess_token\x18\a \x01(\tR\vaccessToken\x12\x1d\n" +
|
"\faccess_token\x18\a \x01(\tR\vaccessToken\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"country_id\x18\b \x01(\x03R\tcountryId\"\xdf\x01\n" +
|
"country_id\x18\b \x01(\x03R\tcountryId\x12%\n" +
|
||||||
|
"\x0eprovider_token\x18\t \x01(\tR\rproviderToken\"\xdf\x01\n" +
|
||||||
"\x12LaunchGameResponse\x12\x1d\n" +
|
"\x12LaunchGameResponse\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"session_id\x18\x01 \x01(\tR\tsessionId\x12\x1d\n" +
|
"session_id\x18\x01 \x01(\tR\tsessionId\x12\x1d\n" +
|
||||||
|
|||||||
@ -144,6 +144,8 @@ message LaunchGameRequest {
|
|||||||
string access_token = 7;
|
string access_token = 7;
|
||||||
// country_id 由 gateway 从当前用户资料注入,供 provider 回调订单按国家统计。
|
// country_id 由 gateway 从当前用户资料注入,供 provider 回调订单按国家统计。
|
||||||
int64 country_id = 8;
|
int64 country_id = 8;
|
||||||
|
// provider_token 是 gateway 签发的短期厂商令牌;仅需要限长令牌的 adapter 使用。
|
||||||
|
string provider_token = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message LaunchGameResponse {
|
message LaunchGameResponse {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -51,7 +52,8 @@ func TestLaunchGameBuildsAMGURL(t *testing.T) {
|
|||||||
DisplayUserID: "420001",
|
DisplayUserID: "420001",
|
||||||
GameID: "amg_1001",
|
GameID: "amg_1001",
|
||||||
RoomID: "room_1",
|
RoomID: "room_1",
|
||||||
AccessToken: "app_access_token_amg",
|
AccessToken: strings.Repeat("full_app_access_token", 40),
|
||||||
|
ProviderToken: "short_amg_provider_token",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("LaunchGame failed: %v", err)
|
t.Fatalf("LaunchGame failed: %v", err)
|
||||||
@ -67,7 +69,7 @@ func TestLaunchGameBuildsAMGURL(t *testing.T) {
|
|||||||
t.Fatalf("AMG launch base mismatch: %s", result.LaunchURL)
|
t.Fatalf("AMG launch base mismatch: %s", result.LaunchURL)
|
||||||
}
|
}
|
||||||
want := map[string]string{
|
want := map[string]string{
|
||||||
"user_token": "app_access_token_amg",
|
"user_token": "short_amg_provider_token",
|
||||||
"app_key": "kzphamrv01",
|
"app_key": "kzphamrv01",
|
||||||
"room_id": "room_1",
|
"room_id": "room_1",
|
||||||
"mini": "1",
|
"mini": "1",
|
||||||
|
|||||||
@ -137,6 +137,7 @@ type LaunchCommand struct {
|
|||||||
RegionID int64
|
RegionID int64
|
||||||
CountryID int64
|
CountryID int64
|
||||||
AccessToken string
|
AccessToken string
|
||||||
|
ProviderToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
type LaunchResult struct {
|
type LaunchResult struct {
|
||||||
@ -308,6 +309,11 @@ func (s *Service) LaunchGame(ctx context.Context, command LaunchCommand) (Launch
|
|||||||
now := s.now()
|
now := s.now()
|
||||||
sessionID := "game_sess_" + strconv.FormatInt(now.UnixMilli(), 10) + "_" + randomHex(6)
|
sessionID := "game_sess_" + strconv.FormatInt(now.UnixMilli(), 10) + "_" + randomHex(6)
|
||||||
token := strings.TrimSpace(command.AccessToken)
|
token := strings.TrimSpace(command.AccessToken)
|
||||||
|
if strings.EqualFold(game.AdapterType, gamedomain.AdapterAMGV1) && strings.TrimSpace(command.ProviderToken) != "" {
|
||||||
|
// AMG 限制 user_token 最长 512 字节;Gateway 签发的厂商令牌保留可验签的 App/用户归因,
|
||||||
|
// 且不会把完整登录 JWT 暴露给厂商。滚动发布期旧 Gateway 没有该字段时仍保留原逻辑。
|
||||||
|
token = strings.TrimSpace(command.ProviderToken)
|
||||||
|
}
|
||||||
if token == "" {
|
if token == "" {
|
||||||
if strings.EqualFold(game.AdapterType, gamedomain.AdapterYomiV4) || strings.EqualFold(game.AdapterType, gamedomain.AdapterLeaderCCV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterZeeOneV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterBaishunV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterVivaGamesV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterReyouV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterZGameV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterAMGV1) {
|
if strings.EqualFold(game.AdapterType, gamedomain.AdapterYomiV4) || strings.EqualFold(game.AdapterType, gamedomain.AdapterLeaderCCV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterZeeOneV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterBaishunV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterVivaGamesV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterReyouV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterZGameV1) || strings.EqualFold(game.AdapterType, gamedomain.AdapterAMGV1) {
|
||||||
return LaunchResult{}, xerr.New(xerr.InvalidArgument, "access token is required for provider game launch")
|
return LaunchResult{}, xerr.New(xerr.InvalidArgument, "access token is required for provider game launch")
|
||||||
|
|||||||
@ -132,6 +132,7 @@ func (s *Server) LaunchGame(ctx context.Context, req *gamev1.LaunchGameRequest)
|
|||||||
RoomID: req.GetRoomId(),
|
RoomID: req.GetRoomId(),
|
||||||
CountryID: req.GetCountryId(),
|
CountryID: req.GetCountryId(),
|
||||||
AccessToken: req.GetAccessToken(),
|
AccessToken: req.GetAccessToken(),
|
||||||
|
ProviderToken: req.GetProviderToken(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerr.ToGRPCError(err)
|
return nil, xerr.ToGRPCError(err)
|
||||||
|
|||||||
@ -2,10 +2,13 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
jwt "github.com/golang-jwt/jwt/v5"
|
jwt "github.com/golang-jwt/jwt/v5"
|
||||||
"hyapp/pkg/appcode"
|
"hyapp/pkg/appcode"
|
||||||
@ -14,6 +17,8 @@ import (
|
|||||||
// ErrAccessTokenExpired 让 transport 精确区分“可通过 refresh 恢复”的 JWT 过期与签名非法/缺失凭证。
|
// ErrAccessTokenExpired 让 transport 精确区分“可通过 refresh 恢复”的 JWT 过期与签名非法/缺失凭证。
|
||||||
var ErrAccessTokenExpired = errors.New("access token expired")
|
var ErrAccessTokenExpired = errors.New("access token expired")
|
||||||
|
|
||||||
|
const gameProviderTokenType = "game_provider"
|
||||||
|
|
||||||
type contextKey string
|
type contextKey string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -57,6 +62,16 @@ func (v *Verifier) VerifyUserID(header string) (int64, error) {
|
|||||||
|
|
||||||
// Verify 校验 access token 并返回 gateway 入口需要的用户状态快照。
|
// Verify 校验 access token 并返回 gateway 入口需要的用户状态快照。
|
||||||
func (v *Verifier) Verify(header string) (Claims, error) {
|
func (v *Verifier) Verify(header string) (Claims, error) {
|
||||||
|
return v.verify(header, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyGameCallbackToken 同时接受历史 access token 与新的短期游戏令牌。
|
||||||
|
// game_provider 令牌只能走厂商回调入口;普通 API 仍由 Verify 拒绝,避免短令牌被当作登录凭证复用。
|
||||||
|
func (v *Verifier) VerifyGameCallbackToken(header string) (Claims, error) {
|
||||||
|
return v.verify(header, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Verifier) verify(header string, allowGameProvider bool) (Claims, error) {
|
||||||
if !strings.HasPrefix(header, "Bearer ") {
|
if !strings.HasPrefix(header, "Bearer ") {
|
||||||
return Claims{}, fmt.Errorf("missing bearer token")
|
return Claims{}, fmt.Errorf("missing bearer token")
|
||||||
}
|
}
|
||||||
@ -82,6 +97,11 @@ func (v *Verifier) Verify(header string) (Claims, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return Claims{}, fmt.Errorf("invalid claims")
|
return Claims{}, fmt.Errorf("invalid claims")
|
||||||
}
|
}
|
||||||
|
tokenType, _ := claims["typ"].(string)
|
||||||
|
tokenType = strings.TrimSpace(tokenType)
|
||||||
|
if tokenType != "" && tokenType != "access" && !(allowGameProvider && tokenType == gameProviderTokenType) {
|
||||||
|
return Claims{}, fmt.Errorf("invalid token type")
|
||||||
|
}
|
||||||
|
|
||||||
rawUserID, ok := claims["user_id"].(string)
|
rawUserID, ok := claims["user_id"].(string)
|
||||||
if !ok || strings.TrimSpace(rawUserID) == "" {
|
if !ok || strings.TrimSpace(rawUserID) == "" {
|
||||||
@ -110,6 +130,32 @@ func (v *Verifier) Verify(header string) (Claims, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IssueGameProviderToken 把已完成入口鉴权的用户身份压缩成厂商专用 JWT。
|
||||||
|
// 令牌只包含回调归因需要的 App、用户、过期时间和随机 ID,避免把不断增长的完整登录 claims 暴露给厂商。
|
||||||
|
func (v *Verifier) IssueGameProviderToken(appCodeValue string, userID int64, ttl time.Duration) (string, error) {
|
||||||
|
if v == nil || len(v.secret) == 0 || userID <= 0 || ttl <= 0 {
|
||||||
|
return "", fmt.Errorf("invalid game provider token input")
|
||||||
|
}
|
||||||
|
var nonce [12]byte
|
||||||
|
if _, err := rand.Read(nonce[:]); err != nil {
|
||||||
|
return "", fmt.Errorf("generate game provider token id: %w", err)
|
||||||
|
}
|
||||||
|
now := time.Now().UTC()
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||||
|
"app_code": appcode.Normalize(appCodeValue),
|
||||||
|
"exp": now.Add(ttl).Unix(),
|
||||||
|
"iat": now.Unix(),
|
||||||
|
"jti": hex.EncodeToString(nonce[:]),
|
||||||
|
"typ": gameProviderTokenType,
|
||||||
|
"user_id": strconv.FormatInt(userID, 10),
|
||||||
|
})
|
||||||
|
signed, err := token.SignedString(v.secret)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("sign game provider token: %w", err)
|
||||||
|
}
|
||||||
|
return signed, nil
|
||||||
|
}
|
||||||
|
|
||||||
func claimUnixSecondsMS(value any) int64 {
|
func claimUnixSecondsMS(value any) int64 {
|
||||||
switch typed := value.(type) {
|
switch typed := value.(type) {
|
||||||
case float64:
|
case float64:
|
||||||
|
|||||||
@ -90,3 +90,22 @@ func TestVerifierDistinguishesExpiredAccessTokenFromInvalidCredentials(t *testin
|
|||||||
t.Fatalf("expired token with another invalid time claim must remain generic unauthorized, got %v", err)
|
t.Fatalf("expired token with another invalid time claim must remain generic unauthorized, got %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGameProviderTokenIsShortAndCallbackOnly(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
verifier := NewVerifier("secret")
|
||||||
|
token, err := verifier.IssueGameProviderToken("fami", 399999999999990001, 24*time.Hour)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("IssueGameProviderToken failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(token) > 512 {
|
||||||
|
t.Fatalf("game provider token length=%d, want <=512", len(token))
|
||||||
|
}
|
||||||
|
claims, err := verifier.VerifyGameCallbackToken("Bearer " + token)
|
||||||
|
if err != nil || claims.AppCode != "fami" || claims.UserID != 399999999999990001 {
|
||||||
|
t.Fatalf("callback token claims mismatch: claims=%+v err=%v", claims, err)
|
||||||
|
}
|
||||||
|
if _, err := verifier.Verify("Bearer " + token); err == nil {
|
||||||
|
t.Fatal("game provider token must not authenticate ordinary App APIs")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -203,6 +203,11 @@ func TestLaunchGameForwardsDisplayUserID(t *testing.T) {
|
|||||||
if gameClient.lastLaunch.GetGameId() != "demo_rocket_001" || gameClient.lastLaunch.GetDisplayUserId() != "100001" || gameClient.lastLaunch.GetRoomId() != "room-1" || gameClient.lastLaunch.GetAccessToken() != accessToken {
|
if gameClient.lastLaunch.GetGameId() != "demo_rocket_001" || gameClient.lastLaunch.GetDisplayUserId() != "100001" || gameClient.lastLaunch.GetRoomId() != "room-1" || gameClient.lastLaunch.GetAccessToken() != accessToken {
|
||||||
t.Fatalf("LaunchGame request mismatch: %+v", gameClient.lastLaunch)
|
t.Fatalf("LaunchGame request mismatch: %+v", gameClient.lastLaunch)
|
||||||
}
|
}
|
||||||
|
providerToken := gameClient.lastLaunch.GetProviderToken()
|
||||||
|
claims, err := auth.NewVerifier("secret").VerifyGameCallbackToken("Bearer " + providerToken)
|
||||||
|
if err != nil || len(providerToken) > 512 || claims.UserID != 42 || claims.AppCode != "lalu" {
|
||||||
|
t.Fatalf("provider token mismatch: length=%d claims=%+v err=%v", len(providerToken), claims, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDiceMatchRenewsAccessTokenWhenTokenNearExpiry(t *testing.T) {
|
func TestDiceMatchRenewsAccessTokenWhenTokenNearExpiry(t *testing.T) {
|
||||||
@ -574,7 +579,10 @@ func TestZGameFixedCallbackRouteIsRawPublicResponse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAMGFixedCallbackRoutesUseAuthorizationTokenAppAndRawResponse(t *testing.T) {
|
func TestAMGFixedCallbackRoutesUseAuthorizationTokenAppAndRawResponse(t *testing.T) {
|
||||||
token := signGatewayTokenWithAppCode(t, "secret", 42, "fami")
|
token, err := auth.NewVerifier("secret").IssueGameProviderToken("fami", 42, 24*time.Hour)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("IssueGameProviderToken failed: %v", err)
|
||||||
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
method string
|
method string
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
diceAccessTokenRenewBefore = 10 * time.Minute
|
diceAccessTokenRenewBefore = 10 * time.Minute
|
||||||
|
gameProviderTokenTTL = 24 * time.Hour
|
||||||
hyappAccessTokenHeader = "X-Hyapp-Access-Token"
|
hyappAccessTokenHeader = "X-Hyapp-Access-Token"
|
||||||
hyappTokenTypeHeader = "X-Hyapp-Token-Type"
|
hyappTokenTypeHeader = "X-Hyapp-Token-Type"
|
||||||
hyappExpiresInHeader = "X-Hyapp-Expires-In"
|
hyappExpiresInHeader = "X-Hyapp-Expires-In"
|
||||||
@ -148,6 +149,13 @@ func (h *Handler) launchGame(writer http.ResponseWriter, request *http.Request)
|
|||||||
httpkit.WriteRPCError(writer, request, err)
|
httpkit.WriteRPCError(writer, request, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// 厂商令牌由已经完成 access-token 鉴权的 gateway 签发;它只带回调归因需要的最小 claims,
|
||||||
|
// game-service 会按 adapter 决定是否使用,避免 AMG 因完整 App JWT 超过 512 字节而拒绝启动。
|
||||||
|
providerToken, err := h.jwtVerifier.IssueGameProviderToken(appcode.FromContext(request.Context()), userID, gameProviderTokenTTL)
|
||||||
|
if err != nil {
|
||||||
|
httpkit.WriteError(writer, request, http.StatusInternalServerError, httpkit.CodeInternalError, "internal server error")
|
||||||
|
return
|
||||||
|
}
|
||||||
resp, err := h.gameClient.LaunchGame(request.Context(), &gamev1.LaunchGameRequest{
|
resp, err := h.gameClient.LaunchGame(request.Context(), &gamev1.LaunchGameRequest{
|
||||||
Meta: gameMeta(request),
|
Meta: gameMeta(request),
|
||||||
UserId: userID,
|
UserId: userID,
|
||||||
@ -157,6 +165,7 @@ func (h *Handler) launchGame(writer http.ResponseWriter, request *http.Request)
|
|||||||
RoomId: body.RoomID,
|
RoomId: body.RoomID,
|
||||||
AccessToken: bearerAccessToken(request),
|
AccessToken: bearerAccessToken(request),
|
||||||
CountryId: userResp.GetUser().GetCountryId(),
|
CountryId: userResp.GetUser().GetCountryId(),
|
||||||
|
ProviderToken: providerToken,
|
||||||
})
|
})
|
||||||
httpkit.Write(writer, request, gameLaunchDataFromProto(resp), err)
|
httpkit.Write(writer, request, gameLaunchDataFromProto(resp), err)
|
||||||
}
|
}
|
||||||
@ -840,12 +849,12 @@ func (h *Handler) withVerifiedCallbackToken(request *http.Request, raw []byte) (
|
|||||||
return request, false
|
return request, false
|
||||||
}
|
}
|
||||||
for _, token := range jwtCandidates {
|
for _, token := range jwtCandidates {
|
||||||
claims, err := h.jwtVerifier.Verify("Bearer " + token)
|
claims, err := h.jwtVerifier.VerifyGameCallbackToken("Bearer " + token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// 游戏回调是公网入口,callback 自带 app_code/header 都不能作为归因事实;只有 gateway 验过签的 HyApp access token
|
// 游戏回调是公网入口,callback 自带 app_code/header 都不能作为归因事实;只有 gateway 验过签的
|
||||||
// 才能覆盖 request context,后续 gameMeta 会把 token 内 app_code/user_id 透传到 game-service RequestMeta。
|
// HyApp access/provider token 才能覆盖 request context,后续 gameMeta 会透传签名内的 app_code/user_id。
|
||||||
return request.WithContext(auth.WithClaims(request.Context(), claims)), true
|
return request.WithContext(auth.WithClaims(request.Context(), claims)), true
|
||||||
}
|
}
|
||||||
return request, false
|
return request, false
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user