feat(payments): trusted platform signal on the session
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 1m7s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m42s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 1m7s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m42s
Record the execution platform (kind vk|telegram|direct + device subtype ios|android|web) on each session, captured at creation and carried gateway->backend as a trusted X-Platform header, so the upcoming store-compliance gate has an unforgeable execution context. - backend.sessions gains nullable platform_kind/platform_subtype columns (migration 00011, CHECK-constrained, jet regenerated); session.Platform captures them at mint, resolve returns them, middleware exposes platform(c). kind is derived from the establish endpoint, never a client field; the account-merge session mint inherits the caller's platform. - gateway derives the platform (VK subtype from the signed vk_platform via vkauth, Telegram/direct best-effort from the client) and injects X-Platform on every authenticated backend call through the request context. - ui submits a best-effort device subtype on the telegram/guest/email login requests (new FBS subtype field); VK is server-derived from the signed params. - an unattributed session is untrusted (view-only); VK/TG self-heal on the next cold-start re-mint, direct/email on re-login. Signal plumbing only, no user-visible change; X-Platform is inert until the gate consumes it.
This commit is contained in:
@@ -211,7 +211,7 @@ type ChatResp struct {
|
||||
// brand-new account's display name and language from the validated launch fields, its
|
||||
// time zone from browserTz (the client's detected "±HH:MM" UTC offset) and, from the
|
||||
// validated launch deep-link startParam, its variant preferences (first contact only).
|
||||
func (c *Client) TelegramAuth(ctx context.Context, externalID, languageCode, username, firstName, browserTz, startParam string) (SessionResp, error) {
|
||||
func (c *Client) TelegramAuth(ctx context.Context, externalID, languageCode, username, firstName, browserTz, startParam, subtype string) (SessionResp, error) {
|
||||
var out SessionResp
|
||||
err := c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/telegram", "", "",
|
||||
map[string]string{
|
||||
@@ -221,6 +221,7 @@ func (c *Client) TelegramAuth(ctx context.Context, externalID, languageCode, use
|
||||
"first_name": firstName,
|
||||
"browser_tz": browserTz,
|
||||
"start_param": startParam,
|
||||
"subtype": subtype,
|
||||
}, &out)
|
||||
return out, err
|
||||
}
|
||||
@@ -230,7 +231,7 @@ func (c *Client) TelegramAuth(ctx context.Context, externalID, languageCode, use
|
||||
// name from displayName (read client-side via VKWebAppGetUserInfo, since VK omits the
|
||||
// name from the signed launch params) and its time zone from browserTz (the client's
|
||||
// detected "±HH:MM" UTC offset). All seeds apply on first contact only.
|
||||
func (c *Client) VKAuth(ctx context.Context, externalID, languageCode, displayName, browserTz string) (SessionResp, error) {
|
||||
func (c *Client) VKAuth(ctx context.Context, externalID, languageCode, displayName, browserTz, subtype string) (SessionResp, error) {
|
||||
var out SessionResp
|
||||
err := c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/vk", "", "",
|
||||
map[string]string{
|
||||
@@ -238,6 +239,7 @@ func (c *Client) VKAuth(ctx context.Context, externalID, languageCode, displayNa
|
||||
"language_code": languageCode,
|
||||
"display_name": displayName,
|
||||
"browser_tz": browserTz,
|
||||
"subtype": subtype,
|
||||
}, &out)
|
||||
return out, err
|
||||
}
|
||||
@@ -290,10 +292,10 @@ func (c *Client) ChatAccessByUser(ctx context.Context, userID string) (ChatAcces
|
||||
|
||||
// GuestAuth provisions a guest account and mints a session, seeding its time zone
|
||||
// from browserTz (the client's detected "±HH:MM" UTC offset).
|
||||
func (c *Client) GuestAuth(ctx context.Context, browserTz string) (SessionResp, error) {
|
||||
func (c *Client) GuestAuth(ctx context.Context, browserTz, subtype string) (SessionResp, error) {
|
||||
var out SessionResp
|
||||
err := c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/guest", "", "",
|
||||
map[string]string{"browser_tz": browserTz}, &out)
|
||||
map[string]string{"browser_tz": browserTz, "subtype": subtype}, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
@@ -307,10 +309,10 @@ func (c *Client) EmailRequest(ctx context.Context, email, browserTz, language st
|
||||
}
|
||||
|
||||
// EmailLogin verifies a login code and mints a session.
|
||||
func (c *Client) EmailLogin(ctx context.Context, email, code string) (SessionResp, error) {
|
||||
func (c *Client) EmailLogin(ctx context.Context, email, code, subtype string) (SessionResp, error) {
|
||||
var out SessionResp
|
||||
err := c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/email/login", "", "",
|
||||
map[string]string{"email": email, "code": code}, &out)
|
||||
map[string]string{"email": email, "code": code, "subtype": subtype}, &out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
@@ -331,16 +333,24 @@ func (c *Client) EmailConfirmLink(ctx context.Context, token string) (ConfirmLin
|
||||
return out, err
|
||||
}
|
||||
|
||||
// ResolveSession maps a token to its account id and guest flag (gateway
|
||||
// session-cache miss). The guest flag lets the edge gate guest-forbidden ops.
|
||||
func (c *Client) ResolveSession(ctx context.Context, token string) (string, bool, error) {
|
||||
// ResolveSession maps a token to its account id, guest flag, and trusted platform
|
||||
// (gateway session-cache miss). The guest flag lets the edge gate guest-forbidden
|
||||
// ops; the platform ("<kind>/<subtype>", empty for an untrusted session) is injected
|
||||
// as X-Platform on the caller's backend requests.
|
||||
func (c *Client) ResolveSession(ctx context.Context, token string) (string, bool, string, error) {
|
||||
var out struct {
|
||||
UserID string `json:"user_id"`
|
||||
IsGuest bool `json:"is_guest"`
|
||||
UserID string `json:"user_id"`
|
||||
IsGuest bool `json:"is_guest"`
|
||||
PlatformKind string `json:"platform_kind"`
|
||||
PlatformSubtype string `json:"platform_subtype"`
|
||||
}
|
||||
err := c.do(ctx, http.MethodPost, "/api/v1/internal/sessions/resolve", "", "",
|
||||
map[string]string{"token": token}, &out)
|
||||
return out.UserID, out.IsGuest, err
|
||||
platform := ""
|
||||
if out.PlatformKind != "" {
|
||||
platform = out.PlatformKind + "/" + out.PlatformSubtype
|
||||
}
|
||||
return out.UserID, out.IsGuest, platform, err
|
||||
}
|
||||
|
||||
// Profile returns the authenticated account's profile.
|
||||
|
||||
Reference in New Issue
Block a user