sdk: loginWithCustom facade + regenerated models
CI / check (push) Failing after 22s
CI / publish (push) Has been skipped

This commit is contained in:
edmand46
2026-08-28 13:36:09 +03:00
parent 68287d212e
commit e6128e54bd
9 changed files with 83 additions and 33 deletions
+41 -4
View File
@@ -1,15 +1,15 @@
/** /**
* AuthService — device-based player authentication. * AuthService — player authentication.
* *
* Handles login via device ID (the primary auth flow for game clients), * Handles login via device ID (the primary auth flow for game clients) or a
* logout (token clearing), and auth state observation. * custom backend webhook, logout (token clearing), and auth state observation.
*/ */
import type { RudderContext } from '../core/context.js'; import type { RudderContext } from '../core/context.js';
import type { RemoteConfigShape } from '../state/RemoteConfigState.js'; import type { RemoteConfigShape } from '../state/RemoteConfigState.js';
import { getOrCreateDeviceId } from '../device/DeviceId.js'; import { getOrCreateDeviceId } from '../device/DeviceId.js';
import { api } from '../generated/api.js'; import { api } from '../generated/api.js';
import type { LoginViaDeviceResponse } from '../generated/auth.js'; import type { LoginViaCustomResponse, LoginViaDeviceResponse } from '../generated/auth.js';
export type AuthState = 'signed-in' | 'signed-out'; export type AuthState = 'signed-in' | 'signed-out';
@@ -24,6 +24,17 @@ export interface LoginWithDeviceOptions {
nickname?: string; nickname?: string;
} }
export interface LoginWithCustomOptions {
/** Arbitrary payload forwarded to the developer's custom auth webhook. */
customData: Record<string, unknown>;
/** Player's region code (default: "global"). */
region?: string;
/** Player's language code (default: "en"). */
language?: string;
/** Optional player nickname; omitted from the request when not set. */
nickname?: string;
}
export class AuthService<TConfig extends RemoteConfigShape = RemoteConfigShape> { export class AuthService<TConfig extends RemoteConfigShape = RemoteConfigShape> {
private readonly listeners = new Set<AuthStateListener>(); private readonly listeners = new Set<AuthStateListener>();
private state: AuthState; private state: AuthState;
@@ -81,6 +92,32 @@ export class AuthService<TConfig extends RemoteConfigShape = RemoteConfigShape>
return response; return response;
} }
/**
* Authenticates via the project's custom authorization webhook and returns
* access + refresh tokens.
*
* The payload is forwarded to the developer's backend; on success, tokens are
* saved to the client's TokenStore.
*/
async loginWithCustom(options: LoginWithCustomOptions): Promise<LoginViaCustomResponse> {
const { customData, region = 'global', language = 'en', nickname } = options;
const response = await api.loginViaCustom(this.ctx, {
key: this.ctx.options.projectKey,
customData,
region,
language,
nickname,
});
this.ctx.options.tokenStore.saveTokens(
response.accessToken ?? '',
response.refreshToken ?? '',
);
await this.startRuntime();
this.setState('signed-in');
return response;
}
/** Clears all stored tokens (logout). */ /** Clears all stored tokens (logout). */
logout(): void { logout(): void {
this.ctx.options.tokenStore.clear(); this.ctx.options.tokenStore.clear();
+9 -4
View File
@@ -1,13 +1,13 @@
// Code generated by apigen. DO NOT EDIT. // Code generated by apigen. DO NOT EDIT.
import type { LoginViaDeviceRequest, LoginViaDeviceResponse, RefreshAccessTokenRequest, RefreshAccessTokenResponse } from './auth.js'; import type { LoginViaCustomRequest, LoginViaCustomResponse, LoginViaDeviceRequest, LoginViaDeviceResponse, RefreshAccessTokenRequest, RefreshAccessTokenResponse } from './auth.js';
import type { AddBattlePassXpRequest, AddBattlePassXpResponse, ClaimBattlePassRewardRequest, ClaimBattlePassRewardResponse, GetBattlePassProgressRequest, GetBattlePassProgressResponse, PurchaseBattlePassPremiumRequest, PurchaseBattlePassPremiumResponse } from './battlepass.js'; import type { AddBattlePassXpRequest, AddBattlePassXpResponse, ClaimBattlePassRewardRequest, ClaimBattlePassRewardResponse, GetBattlePassProgressRequest, GetBattlePassProgressResponse, PurchaseBattlePassPremiumRequest, PurchaseBattlePassPremiumResponse } from './battlepass.js';
import type { ListCatalogItemsResponse } from './catalog.js'; import type { ListCatalogItemsResponse } from './catalog.js';
import type { GetInventoryResponse } from './inventory.js'; import type { GetInventoryResponse } from './inventory.js';
import type { GetRankingResponse, SubmitScoreRequest } from './leaderboards.js'; import type { GetRankingResponse, SubmitScoreRequest } from './leaderboards.js';
import type { PlayerProfile } from './player.js'; import type { PlayerProfile } from './player.js';
import type { GetProjectStorageResponse, UpdateProjectStorageRequest } from './project-storage.js'; import type { GetProjectStorageResponse, UpdateProjectStorageRequest } from './project-storage.js';
import type { ClaimQuestRequest, ClaimQuestResponse, ListQuestsRequest, ListQuestsResponse, ReportQuestProgressRequest, ReportQuestProgressResponse } from './quests.js'; import type { ClaimQuestRequest, ClaimQuestResponse, ListQuestsResponse, ReportQuestProgressRequest, ReportQuestProgressResponse } from './quests.js';
import type { ListRemoteConfigsResponse, RemoteConfig } from './remote-config.js'; import type { ListRemoteConfigsResponse, RemoteConfig } from './remote-config.js';
import type { GetScenarioRunRequest, GetScenarioRunResponse, HandleScenarioCallbackRequest, HandleScenarioCallbackResponse, TriggerScenarioRequest, TriggerScenarioResponse, UpdateScenarioCounterRequest, UpdateScenarioCounterResponse } from './scenarios.js'; import type { GetScenarioRunRequest, GetScenarioRunResponse, HandleScenarioCallbackRequest, HandleScenarioCallbackResponse, TriggerScenarioRequest, TriggerScenarioResponse, UpdateScenarioCounterRequest, UpdateScenarioCounterResponse } from './scenarios.js';
import type { GetStorageResponse, UpdateStorageRequest } from './storage.js'; import type { GetStorageResponse, UpdateStorageRequest } from './storage.js';
@@ -131,9 +131,8 @@ export const api = {
listQuests: ( listQuests: (
t: Transport, t: Transport,
body: ListQuestsRequest,
): Promise<ListQuestsResponse> => ): Promise<ListQuestsResponse> =>
t.request<ListQuestsResponse>('POST', '/sdk/v1/quests/list', body), t.request<ListQuestsResponse>('POST', '/sdk/v1/quests/list'),
listSdkRemoteConfigs: ( listSdkRemoteConfigs: (
t: Transport, t: Transport,
@@ -145,6 +144,12 @@ export const api = {
): Promise<ListStoresResponse> => ): Promise<ListStoresResponse> =>
t.request<ListStoresResponse>('GET', '/sdk/v1/stores'), t.request<ListStoresResponse>('GET', '/sdk/v1/stores'),
loginViaCustom: (
t: Transport,
body: LoginViaCustomRequest,
): Promise<LoginViaCustomResponse> =>
t.request<LoginViaCustomResponse>('POST', '/sdk/v1/authorization/custom', body),
loginViaDevice: ( loginViaDevice: (
t: Transport, t: Transport,
body: LoginViaDeviceRequest, body: LoginViaDeviceRequest,
+13
View File
@@ -1,5 +1,18 @@
// Code generated by apigen. DO NOT EDIT. // Code generated by apigen. DO NOT EDIT.
export interface LoginViaCustomRequest {
"customData"?: { [key: string]: unknown };
"key"?: string;
"language"?: string;
"nickname"?: string;
"region"?: string;
}
export interface LoginViaCustomResponse {
"accessToken"?: string;
"refreshToken"?: string;
}
export interface LoginViaDeviceRequest { export interface LoginViaDeviceRequest {
"deviceId"?: string; "deviceId"?: string;
"key"?: string; "key"?: string;
+3 -9
View File
@@ -1,6 +1,6 @@
// Code generated by apigen. DO NOT EDIT. // Code generated by apigen. DO NOT EDIT.
import type { ExecutionPlan } from './common.js'; import type { ExecutionPlan, Reward } from './common.js';
export interface AddBattlePassXpRequest { export interface AddBattlePassXpRequest {
"amount"?: number; "amount"?: number;
@@ -18,24 +18,18 @@ export interface AddBattlePassXpResponse {
"xp"?: number; "xp"?: number;
} }
export interface BattlePassReward {
"amount"?: number;
"currency"?: string;
"itemId"?: string;
}
export interface ClaimBattlePassRewardRequest { export interface ClaimBattlePassRewardRequest {
"level"?: number; "level"?: number;
"nodeId"?: string; "nodeId"?: string;
"runId"?: string; "runId"?: string;
"scenarioId"?: string; "scenarioId"?: string;
"track"?: string; "track"?: "free" | "premium";
} }
export interface ClaimBattlePassRewardResponse { export interface ClaimBattlePassRewardResponse {
"alreadyClaimed"?: boolean; "alreadyClaimed"?: boolean;
"error"?: string; "error"?: string;
"granted"?: BattlePassReward[]; "granted"?: Reward[];
"success"?: boolean; "success"?: boolean;
} }
+8
View File
@@ -13,6 +13,8 @@ export interface BoundaryNode {
export interface ErrorResponse { export interface ErrorResponse {
"code"?: "early_completion" | "forbidden" | "level_not_reached" | "node_not_active" | "objectives_incomplete" | "run_expired" | "run_not_active" | "scenario_not_active" | "unknown_run"; "code"?: "early_completion" | "forbidden" | "level_not_reached" | "node_not_active" | "objectives_incomplete" | "run_expired" | "run_not_active" | "scenario_not_active" | "unknown_run";
"error"?: string; "error"?: string;
"index"?: number;
"playerId"?: string;
"requestId"?: string; "requestId"?: string;
} }
@@ -42,3 +44,9 @@ export interface PlanEdge {
"targetHandle"?: string; "targetHandle"?: string;
} }
export interface Reward {
"amount"?: number;
"currency"?: string;
"itemId"?: string;
}
+5 -12
View File
@@ -1,5 +1,7 @@
// Code generated by apigen. DO NOT EDIT. // Code generated by apigen. DO NOT EDIT.
import type { Reward } from './common.js';
export interface ClaimQuestRequest { export interface ClaimQuestRequest {
"questId"?: string; "questId"?: string;
} }
@@ -7,13 +9,10 @@ export interface ClaimQuestRequest {
export interface ClaimQuestResponse { export interface ClaimQuestResponse {
"alreadyClaimed"?: boolean; "alreadyClaimed"?: boolean;
"error"?: string; "error"?: string;
"granted"?: QuestReward[]; "granted"?: Reward[];
"success"?: boolean; "success"?: boolean;
} }
export interface ListQuestsRequest {
}
export interface ListQuestsResponse { export interface ListQuestsResponse {
"quests"?: Quest[]; "quests"?: Quest[];
} }
@@ -22,8 +21,8 @@ export interface Quest {
"id"?: string; "id"?: string;
"name"?: string; "name"?: string;
"objectives"?: QuestObjectiveProgress[]; "objectives"?: QuestObjectiveProgress[];
"rewards"?: QuestReward[]; "rewards"?: Reward[];
"status"?: string; "status"?: "active" | "claimed" | "completed";
} }
export interface QuestObjectiveProgress { export interface QuestObjectiveProgress {
@@ -34,12 +33,6 @@ export interface QuestObjectiveProgress {
"target"?: number; "target"?: number;
} }
export interface QuestReward {
"amount"?: number;
"currency"?: string;
"itemId"?: string;
}
export interface ReportQuestProgressRequest { export interface ReportQuestProgressRequest {
"amount"?: number; "amount"?: number;
"metric"?: string; "metric"?: string;
+1 -1
View File
@@ -14,6 +14,6 @@ export interface RemoteConfig {
"projectId"?: string; "projectId"?: string;
"updatedAt"?: string; "updatedAt"?: string;
"value"?: string; "value"?: string;
"valueType"?: string; "valueType"?: "bool" | "float" | "int" | "json" | "string";
} }
+1 -1
View File
@@ -9,7 +9,7 @@ export interface GetScenarioRunRequest {
export interface GetScenarioRunResponse { export interface GetScenarioRunResponse {
"plan"?: ExecutionPlan; "plan"?: ExecutionPlan;
"runId"?: string; "runId"?: string;
"status"?: string; "status"?: "active" | "completed" | "expired";
} }
export interface HandleScenarioCallbackRequest { export interface HandleScenarioCallbackRequest {
+2 -2
View File
@@ -22,6 +22,7 @@ export type {
AuthService, AuthService,
AuthState, AuthState,
AuthStateListener, AuthStateListener,
LoginWithCustomOptions,
LoginWithDeviceOptions, LoginWithDeviceOptions,
} from './auth/AuthService.js'; } from './auth/AuthService.js';
export type { LeaderboardsService, LeaderboardHandle } from './leaderboards/LeaderboardsService.js'; export type { LeaderboardsService, LeaderboardHandle } from './leaderboards/LeaderboardsService.js';
@@ -88,14 +89,13 @@ export type {
ProjectStorageUpdateItem, ProjectStorageUpdateItem,
} from './generated/project-storage.js'; } from './generated/project-storage.js';
export type { RemoteConfig } from './generated/remote-config.js'; export type { RemoteConfig } from './generated/remote-config.js';
export type { Reward } from './generated/common.js';
export type { export type {
Quest, Quest,
QuestObjectiveProgress, QuestObjectiveProgress,
QuestReward,
ClaimQuestResponse, ClaimQuestResponse,
} from './generated/quests.js'; } from './generated/quests.js';
export type { export type {
BattlePassReward,
ClaimedTier, ClaimedTier,
AddBattlePassXpRequest, AddBattlePassXpRequest,
AddBattlePassXpResponse, AddBattlePassXpResponse,