@open-core/identity 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,149 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { injectable, inject } from "tsyringe";
14
+ import { Server } from "@open-core/framework";
15
+ import { v4 as uuidv4 } from "uuid";
16
+ import { IDENTITY_OPTIONS } from "../../tokens";
17
+ import { IdentityStore } from "../../contracts";
18
+ import bcrypt from "bcryptjs";
19
+ /**
20
+ * Authentication provider for username and password credentials.
21
+ *
22
+ * This provider implements the framework's {@link Server.AuthProviderContract} using
23
+ * bcrypt for password hashing and validation. It requires an implementation
24
+ * of {@link IdentityStore} that supports username-based lookups.
25
+ *
26
+ * @injectable
27
+ * @public
28
+ */
29
+ let CredentialsAuthProvider = class CredentialsAuthProvider extends Server.AuthProviderContract {
30
+ /**
31
+ * Initializes a new instance of the CredentialsAuthProvider.
32
+ *
33
+ * @param options - Identity system configuration options.
34
+ * @param store - Persistence layer for account and credential data.
35
+ */
36
+ constructor(options, store) {
37
+ super();
38
+ this.options = options;
39
+ this.store = store;
40
+ /** Cost factor for bcrypt hashing */
41
+ this.saltRounds = 10;
42
+ }
43
+ /**
44
+ * Authenticates a player using a username and password.
45
+ *
46
+ * @param player - The framework player entity.
47
+ * @param credentials - Object containing `username` and `password` strings.
48
+ * @returns A promise resolving to the authentication result.
49
+ */
50
+ async authenticate(player, credentials) {
51
+ const username = credentials.username;
52
+ const password = credentials.password;
53
+ if (!username || !password) {
54
+ return { success: false, error: "Username and password are required" };
55
+ }
56
+ const account = await this.store.findByUsername(username);
57
+ if (!account) {
58
+ return { success: false, error: "Invalid credentials" };
59
+ }
60
+ const passwordHash = account.passwordHash;
61
+ if (!passwordHash) {
62
+ return { success: false, error: "Account has no password set" };
63
+ }
64
+ const isValid = await bcrypt.compare(password, passwordHash);
65
+ if (!isValid) {
66
+ return { success: false, error: "Invalid credentials" };
67
+ }
68
+ if (this.isBanned(account)) {
69
+ return { success: false, error: account.banReason ?? "Account is banned" };
70
+ }
71
+ player.linkAccount(account.linkedId);
72
+ return { success: true, accountID: account.linkedId };
73
+ }
74
+ /**
75
+ * Registers a new account with a username and password.
76
+ *
77
+ * @param player - The framework player entity.
78
+ * @param credentials - Object containing `username` and `password` strings.
79
+ * @returns A promise resolving to the registration result.
80
+ */
81
+ async register(player, credentials) {
82
+ const username = credentials.username;
83
+ const password = credentials.password;
84
+ if (!username || !password) {
85
+ return { success: false, error: "Username and password are required" };
86
+ }
87
+ const existing = await this.store.findByUsername(username);
88
+ if (existing) {
89
+ return { success: false, error: "Username already taken" };
90
+ }
91
+ const passwordHash = await bcrypt.hash(password, this.saltRounds);
92
+ const identifiers = player.getIdentifiers();
93
+ const primaryIdentifier = identifiers[0] || `internal:${username}`;
94
+ const account = await this.store.create({
95
+ username,
96
+ passwordHash,
97
+ identifier: primaryIdentifier,
98
+ linkedId: uuidv4(),
99
+ roleName: this.options.principal.defaultRole || "user",
100
+ });
101
+ player.linkAccount(account.linkedId);
102
+ return { success: true, accountID: account.linkedId, isNewAccount: true };
103
+ }
104
+ /**
105
+ * Validates if the player's current linked account session is still active.
106
+ *
107
+ * @param player - The framework player entity.
108
+ * @returns A promise resolving to the validation result.
109
+ */
110
+ async validateSession(player) {
111
+ const accountId = player.accountID;
112
+ if (!accountId)
113
+ return { success: false, error: "Not authenticated" };
114
+ const account = await this.store.findByLinkedId(accountId);
115
+ if (!account || this.isBanned(account)) {
116
+ return { success: false, error: "Session invalid or account banned" };
117
+ }
118
+ return { success: true, accountID: account.linkedId };
119
+ }
120
+ /**
121
+ * Performs logout logic for the player.
122
+ *
123
+ * @param player - The framework player entity.
124
+ */
125
+ async logout(player) {
126
+ // Session state is managed by the framework.
127
+ }
128
+ /**
129
+ * Internal helper to determine if an account is currently prohibited.
130
+ *
131
+ * @param account - The account to check.
132
+ * @returns True if the account is banned and the ban hasn't expired.
133
+ * @internal
134
+ */
135
+ isBanned(account) {
136
+ if (!account.isBanned)
137
+ return false;
138
+ if (account.banExpiresAt && account.banExpiresAt < new Date()) {
139
+ return false;
140
+ }
141
+ return true;
142
+ }
143
+ };
144
+ CredentialsAuthProvider = __decorate([
145
+ injectable(),
146
+ __param(0, inject(IDENTITY_OPTIONS)),
147
+ __metadata("design:paramtypes", [Object, IdentityStore])
148
+ ], CredentialsAuthProvider);
149
+ export { CredentialsAuthProvider };
@@ -0,0 +1,82 @@
1
+ import { Server } from "@open-core/framework";
2
+ import { IdentityStore } from "../../contracts";
3
+ import type { IdentityOptions } from "../../types";
4
+ /**
5
+ * Result structure for authentication operations.
6
+ *
7
+ * @public
8
+ */
9
+ interface AuthResult {
10
+ /** Indicates if the operation was successful */
11
+ success: boolean;
12
+ /** The unique identifier for the authenticated account */
13
+ accountID?: string;
14
+ /** Error message if the operation failed */
15
+ error?: string;
16
+ /** Indicates if a new account was created during the process */
17
+ isNewAccount?: boolean;
18
+ }
19
+ /**
20
+ * Local Authentication Provider for the OpenCore Identity System.
21
+ *
22
+ * This provider implements the framework's {@link Server.AuthProviderContract} and
23
+ * handles the logic for local (identifier-based) authentication strategies.
24
+ *
25
+ * @injectable
26
+ * @public
27
+ */
28
+ export declare class LocalAuthProvider extends Server.AuthProviderContract {
29
+ private readonly options;
30
+ private readonly store;
31
+ private readonly config;
32
+ /**
33
+ * Initializes a new instance of the IdentityAuthProvider.
34
+ *
35
+ * @param options - Identity system configuration options.
36
+ * @param store - Persistence layer for account data.
37
+ * @param config - Framework configuration service.
38
+ */
39
+ constructor(options: IdentityOptions, store: IdentityStore, config: Server.ConfigService);
40
+ /**
41
+ * Authenticates a player based on the configured strategy.
42
+ *
43
+ * @param player - The player to authenticate.
44
+ * @param credentials - Optional credentials (used in API or credentials mode).
45
+ * @returns A promise resolving to an {@link AuthResult}.
46
+ */
47
+ authenticate(player: Server.Player, credentials: Record<string, unknown>): Promise<AuthResult>;
48
+ /**
49
+ * Registers a new player identity.
50
+ *
51
+ * @param player - The player to register.
52
+ * @param credentials - Registration data.
53
+ * @returns A promise resolving to an {@link AuthResult}.
54
+ */
55
+ register(player: Server.Player, credentials: Record<string, unknown>): Promise<AuthResult>;
56
+ /**
57
+ * Validates the current session for a player.
58
+ *
59
+ * @param player - The player whose session to validate.
60
+ * @returns A promise resolving to an {@link AuthResult}.
61
+ */
62
+ validateSession(player: Server.Player): Promise<AuthResult>;
63
+ /**
64
+ * Clears the authentication state for a player.
65
+ *
66
+ * @param player - The player to log out.
67
+ */
68
+ logout(player: Server.Player): Promise<void>;
69
+ /**
70
+ * Internal implementation for local authentication strategy.
71
+ *
72
+ * @internal
73
+ */
74
+ private authenticateLocally;
75
+ /**
76
+ * Internal implementation for API-based authentication strategy.
77
+ *
78
+ * @internal
79
+ */
80
+ private authenticateViaApi;
81
+ }
82
+ export {};
@@ -0,0 +1,151 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { injectable, inject } from "tsyringe";
14
+ import { Server } from "@open-core/framework";
15
+ import { v4 as uuidv4 } from "uuid";
16
+ import { IDENTITY_OPTIONS } from "../../tokens";
17
+ import { IdentityStore } from "../../contracts";
18
+ /**
19
+ * Local Authentication Provider for the OpenCore Identity System.
20
+ *
21
+ * This provider implements the framework's {@link Server.AuthProviderContract} and
22
+ * handles the logic for local (identifier-based) authentication strategies.
23
+ *
24
+ * @injectable
25
+ * @public
26
+ */
27
+ let LocalAuthProvider = class LocalAuthProvider extends Server.AuthProviderContract {
28
+ /**
29
+ * Initializes a new instance of the IdentityAuthProvider.
30
+ *
31
+ * @param options - Identity system configuration options.
32
+ * @param store - Persistence layer for account data.
33
+ * @param config - Framework configuration service.
34
+ */
35
+ constructor(options, store, config) {
36
+ super();
37
+ this.options = options;
38
+ this.store = store;
39
+ this.config = config;
40
+ }
41
+ /**
42
+ * Authenticates a player based on the configured strategy.
43
+ *
44
+ * @param player - The player to authenticate.
45
+ * @param credentials - Optional credentials (used in API or credentials mode).
46
+ * @returns A promise resolving to an {@link AuthResult}.
47
+ */
48
+ async authenticate(player, credentials) {
49
+ try {
50
+ if (this.options.auth.mode === "api") {
51
+ return await this.authenticateViaApi(player, credentials);
52
+ }
53
+ return await this.authenticateLocally(player);
54
+ }
55
+ catch (error) {
56
+ return {
57
+ success: false,
58
+ error: error instanceof Error ? error.message : "Internal authentication error",
59
+ };
60
+ }
61
+ }
62
+ /**
63
+ * Registers a new player identity.
64
+ *
65
+ * @param player - The player to register.
66
+ * @param credentials - Registration data.
67
+ * @returns A promise resolving to an {@link AuthResult}.
68
+ */
69
+ async register(player, credentials) {
70
+ return { success: false, error: "Registration not implemented in current mode" };
71
+ }
72
+ /**
73
+ * Validates the current session for a player.
74
+ *
75
+ * @param player - The player whose session to validate.
76
+ * @returns A promise resolving to an {@link AuthResult}.
77
+ */
78
+ async validateSession(player) {
79
+ const accountId = player.accountID;
80
+ if (!accountId)
81
+ return { success: false, error: "No active session" };
82
+ const account = await this.store.findByLinkedId(accountId);
83
+ if (!account)
84
+ return { success: false, error: "Account no longer exists" };
85
+ if (account.isBanned && (!account.banExpiresAt || account.banExpiresAt > new Date())) {
86
+ return { success: false, error: account.banReason ?? "Account is banned" };
87
+ }
88
+ return { success: true, accountID: account.linkedId };
89
+ }
90
+ /**
91
+ * Clears the authentication state for a player.
92
+ *
93
+ * @param player - The player to log out.
94
+ */
95
+ async logout(player) {
96
+ // Session state is managed by the framework's player entity.
97
+ }
98
+ /**
99
+ * Internal implementation for local authentication strategy.
100
+ *
101
+ * @internal
102
+ */
103
+ async authenticateLocally(player) {
104
+ const primaryType = this.options.auth.primaryIdentifier || "license";
105
+ const identifiers = player.getIdentifiers();
106
+ const identifierValue = identifiers.find(id => id.startsWith(`${primaryType}:`));
107
+ if (!identifierValue) {
108
+ return { success: false, error: `Missing required identifier: ${primaryType}` };
109
+ }
110
+ let account = await this.store.findByIdentifier(identifierValue);
111
+ let isNew = false;
112
+ if (!account) {
113
+ if (this.options.auth.autoCreate === false) {
114
+ return { success: false, error: "Account not found and auto-create is disabled" };
115
+ }
116
+ account = await this.store.create({
117
+ identifier: identifierValue,
118
+ linkedId: uuidv4(),
119
+ roleName: "user",
120
+ });
121
+ isNew = true;
122
+ }
123
+ if (account.isBanned) {
124
+ if (account.banExpiresAt && account.banExpiresAt < new Date()) {
125
+ await this.store.setBan(account.id, false);
126
+ }
127
+ else {
128
+ return {
129
+ success: false,
130
+ error: account.banReason ?? "Account is banned",
131
+ };
132
+ }
133
+ }
134
+ player.linkAccount(account.linkedId);
135
+ return { success: true, accountID: account.linkedId, isNewAccount: isNew };
136
+ }
137
+ /**
138
+ * Internal implementation for API-based authentication strategy.
139
+ *
140
+ * @internal
141
+ */
142
+ async authenticateViaApi(player, credentials) {
143
+ return { success: false, error: "API Auth Mode not yet fully implemented" };
144
+ }
145
+ };
146
+ LocalAuthProvider = __decorate([
147
+ injectable(),
148
+ __param(0, inject(IDENTITY_OPTIONS)),
149
+ __metadata("design:paramtypes", [Object, IdentityStore, Server.ConfigService])
150
+ ], LocalAuthProvider);
151
+ export { LocalAuthProvider };
File without changes
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,50 @@
1
+ import { Server } from "@open-core/framework";
2
+ import type { IdentityOptions } from "../../types";
3
+ /**
4
+ * Principal provider that resolves roles and permissions from an external HTTP API.
5
+ *
6
+ * This provider implements the framework's {@link Server.PrincipalProviderContract} by
7
+ * performing network requests to a remote service. It includes an in-memory cache
8
+ * to optimize frequent authorization checks.
9
+ *
10
+ * @injectable
11
+ * @public
12
+ */
13
+ export declare class ApiPrincipalProvider extends Server.PrincipalProviderContract {
14
+ private readonly options;
15
+ private readonly http;
16
+ /**
17
+ * In-memory cache for resolved principals.
18
+ * Key: clientId (number)
19
+ */
20
+ private readonly cache;
21
+ /** Cache TTL in milliseconds */
22
+ private readonly cacheTtl;
23
+ /**
24
+ * Initializes a new instance of the ApiPrincipalProvider.
25
+ *
26
+ * @param options - Identity system configuration options.
27
+ * @param http - Framework HTTP service for remote communication.
28
+ */
29
+ constructor(options: IdentityOptions, http: Server.HttpService);
30
+ /**
31
+ * Resolves the Principal for a connected player via external API.
32
+ *
33
+ * @param player - The framework player entity.
34
+ * @returns A promise resolving to the {@link Server.Principal} or null if not authenticated.
35
+ */
36
+ getPrincipal(player: Server.Player): Promise<Server.Principal | null>;
37
+ /**
38
+ * Forces a refresh of the cached principal data from the API.
39
+ *
40
+ * @param player - The player whose principal should be refreshed.
41
+ */
42
+ refreshPrincipal(player: Server.Player): Promise<void>;
43
+ /**
44
+ * Resolves a principal for offline workflows using a stable account ID via API.
45
+ *
46
+ * @param linkedID - The linked account identifier.
47
+ * @returns A promise resolving to the principal or null.
48
+ */
49
+ getPrincipalByLinkedID(linkedID: string): Promise<Server.Principal | null>;
50
+ }
@@ -0,0 +1,84 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ import { injectable, inject } from "tsyringe";
14
+ import { Server } from "@open-core/framework";
15
+ import { IDENTITY_OPTIONS } from "../../tokens";
16
+ /**
17
+ * Principal provider that resolves roles and permissions from an external HTTP API.
18
+ *
19
+ * This provider implements the framework's {@link Server.PrincipalProviderContract} by
20
+ * performing network requests to a remote service. It includes an in-memory cache
21
+ * to optimize frequent authorization checks.
22
+ *
23
+ * @injectable
24
+ * @public
25
+ */
26
+ let ApiPrincipalProvider = class ApiPrincipalProvider extends Server.PrincipalProviderContract {
27
+ /**
28
+ * Initializes a new instance of the ApiPrincipalProvider.
29
+ *
30
+ * @param options - Identity system configuration options.
31
+ * @param http - Framework HTTP service for remote communication.
32
+ */
33
+ constructor(options, http) {
34
+ super();
35
+ this.options = options;
36
+ this.http = http;
37
+ /**
38
+ * In-memory cache for resolved principals.
39
+ * Key: clientId (number)
40
+ */
41
+ this.cache = new Map();
42
+ this.cacheTtl = options.principal.cacheTtl ?? 300000;
43
+ }
44
+ /**
45
+ * Resolves the Principal for a connected player via external API.
46
+ *
47
+ * @param player - The framework player entity.
48
+ * @returns A promise resolving to the {@link Server.Principal} or null if not authenticated.
49
+ */
50
+ async getPrincipal(player) {
51
+ const cached = this.cache.get(player.clientID);
52
+ if (cached && cached.expiresAt > Date.now())
53
+ return cached.principal;
54
+ const linkedId = player.accountID;
55
+ if (!linkedId)
56
+ return null;
57
+ // Placeholder: Implementation would use this.http.get(...)
58
+ return null;
59
+ }
60
+ /**
61
+ * Forces a refresh of the cached principal data from the API.
62
+ *
63
+ * @param player - The player whose principal should be refreshed.
64
+ */
65
+ async refreshPrincipal(player) {
66
+ this.cache.delete(player.clientID);
67
+ await this.getPrincipal(player);
68
+ }
69
+ /**
70
+ * Resolves a principal for offline workflows using a stable account ID via API.
71
+ *
72
+ * @param linkedID - The linked account identifier.
73
+ * @returns A promise resolving to the principal or null.
74
+ */
75
+ async getPrincipalByLinkedID(linkedID) {
76
+ return null;
77
+ }
78
+ };
79
+ ApiPrincipalProvider = __decorate([
80
+ injectable(),
81
+ __param(0, inject(IDENTITY_OPTIONS)),
82
+ __metadata("design:paramtypes", [Object, Server.HttpService])
83
+ ], ApiPrincipalProvider);
84
+ export { ApiPrincipalProvider };
@@ -0,0 +1,77 @@
1
+ import { Server } from "@open-core/framework";
2
+ import { IdentityStore, RoleStore } from "../../contracts";
3
+ import type { IdentityOptions } from "../../types";
4
+ /**
5
+ * Authorization provider implementation for the OpenCore Framework.
6
+ *
7
+ * This provider resolves player principals (roles and permissions) by
8
+ * interacting with the configured {@link IdentityStore} and {@link RoleStore}.
9
+ * It includes a high-performance in-memory cache to minimize database
10
+ * overhead during frequent security checks (e.g., in `@Guard` decorators).
11
+ *
12
+ * @injectable
13
+ * @public
14
+ */
15
+ export declare class IdentityPrincipalProvider extends Server.PrincipalProviderContract {
16
+ private readonly options;
17
+ private readonly accountStore;
18
+ private readonly roleStore?;
19
+ /**
20
+ * In-memory cache for resolved principals.
21
+ * Key: clientId (number)
22
+ */
23
+ private readonly cache;
24
+ /** Cache TTL in milliseconds */
25
+ private readonly cacheTtl;
26
+ /**
27
+ * Initializes a new instance of the IdentityPrincipalProvider.
28
+ *
29
+ * @param options - Identity system configuration options.
30
+ * @param accountStore - Persistence layer for account data.
31
+ * @param roleStore - Optional persistence layer for dynamic roles.
32
+ */
33
+ constructor(options: IdentityOptions, accountStore: IdentityStore, roleStore?: RoleStore | undefined);
34
+ /**
35
+ * Resolves the security Principal for a connected player.
36
+ *
37
+ * This method first checks the internal cache. If missing or expired,
38
+ * it resolves the account and its effective permissions.
39
+ *
40
+ * @param player - The framework player entity.
41
+ * @returns A promise resolving to the {@link Server.Principal} or null if not authenticated.
42
+ */
43
+ getPrincipal(player: Server.Player): Promise<Server.Principal | null>;
44
+ /**
45
+ * Invalidates the cache and re-resolves the principal for a player.
46
+ *
47
+ * @param player - The player whose principal should be refreshed.
48
+ */
49
+ refreshPrincipal(player: Server.Player): Promise<void>;
50
+ /**
51
+ * Resolves a principal for offline workflows using a stable account ID.
52
+ *
53
+ * @param linkedID - The linked account identifier.
54
+ * @returns A promise resolving to the principal or null.
55
+ */
56
+ getPrincipalByLinkedID(linkedID: string): Promise<Server.Principal | null>;
57
+ /**
58
+ * Internal logic to resolve effective permissions and construct the Principal.
59
+ *
60
+ * @param linkedId - The stable account ID.
61
+ * @returns Resolves the role, merges permissions, and returns the Principal.
62
+ * @internal
63
+ */
64
+ private resolvePrincipal;
65
+ /**
66
+ * Merges role-based permissions with account-specific overrides.
67
+ *
68
+ * Overrides starting with '-' are removed, and those starting with '+'
69
+ * (or without prefix) are added to the final set.
70
+ *
71
+ * @param base - Base permissions from the role.
72
+ * @param overrides - Custom overrides from the account.
73
+ * @returns The unified list of effective permissions.
74
+ * @internal
75
+ */
76
+ private mergePermissions;
77
+ }