fix(sdk): remove the legacy ts_idpclient package and switch app integrations to @idp.global/sdk

This commit is contained in:
2026-05-18 16:00:28 +00:00
parent 91f06ccae1
commit b31a10b48b
17 changed files with 666 additions and 1360 deletions
+38 -21
View File
@@ -1,17 +1,4 @@
{
"npmci": {
"npmGlobalTools": [],
"dockerRegistryRepoMap": {
"registry.gitlab.com": "code.foss.global/idp.global/app"
},
"dockerBuildargEnvMap": {
"NPMCI_TOKEN_NPM2": "NPMCI_TOKEN_NPM2"
},
"npmRegistryUrl": "registry.npmjs.org"
},
"tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
},
"@git.zone/cli": {
"projectType": "website",
"module": {
@@ -45,11 +32,22 @@
"user sessions"
]
},
"services": ["mongodb", "minio"],
"services": [
"mongodb",
"minio"
],
"release": {
"registries": ["https://verdaccio.lossless.digital"],
"accessLevel": "public"
}
"targets": {
"npm": {
"registries": [
"https://verdaccio.lossless.digital",
"https://registry.npmjs.org"
],
"accessLevel": "public"
}
}
},
"schemaVersion": 2
},
"@git.zone/tsbundle": {
"bundles": [
@@ -59,7 +57,10 @@
"outputMode": "bundle",
"bundler": "esbuild",
"production": true,
"includeFiles": ["./html/index.html", "./assets/**/*"]
"includeFiles": [
"./html/index.html",
"./assets/**/*"
]
}
]
},
@@ -83,16 +84,32 @@
"name": "website",
"from": "./ts_web/index.ts",
"to": "./dist_serve/bundle.js",
"watchPatterns": ["./ts_web/**/*"],
"watchPatterns": [
"./ts_web/**/*"
],
"triggerReload": false
},
{
"name": "html",
"from": "./html/index.html",
"to": "./dist_serve/index.html",
"watchPatterns": ["./html/**/*"],
"watchPatterns": [
"./html/**/*"
],
"triggerReload": false
}
]
},
"@git.zone/tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
},
"@ship.zone/szci": {
"npmGlobalTools": [],
"dockerRegistryRepoMap": {
"registry.gitlab.com": "code.foss.global/idp.global/app"
},
"dockerBuildargEnvMap": {
"NPMCI_TOKEN_NPM2": "NPMCI_TOKEN_NPM2"
}
}
}
}
+11
View File
@@ -1,5 +1,16 @@
# Changelog
## Pending
### Fixes
- remove the legacy ts_idpclient package and switch app integrations to @idp.global/sdk (sdk)
- Deletes the in-repo @idp.global/client implementation and publishing metadata under ts_idpclient.
- Updates the web app to import the browser client from @idp.global/sdk/browser instead of the local build output.
- Replaces local file dependencies for @idp.global/catalog and @idp.global/interfaces with published package versions and adds @idp.global/sdk as a runtime dependency.
- Refreshes README and story references to document the shared SDK package and browser entrypoint.
- Updates project release/config metadata to the newer schema and registry target format.
## 2026-04-20 - 1.21.0 - feat(reception)
add passport device authentication flows and alert delivery management
+13 -12
View File
@@ -17,20 +17,21 @@
"author": "Task Venture Capital GmbH",
"license": "MIT",
"dependencies": {
"@api.global/typedrequest": "^3.3.0",
"@api.global/typedrequest": "^3.3.1",
"@api.global/typedrequest-interfaces": "^3.0.19",
"@api.global/typedserver": "^8.4.6",
"@api.global/typedsocket": "^4.1.2",
"@api.global/typedsocket": "^4.1.3",
"@consent.software/catalog": "^2.0.1",
"@design.estate/dees-catalog": "^3.81.0",
"@design.estate/dees-domtools": "^2.5.6",
"@design.estate/dees-element": "^2.2.4",
"@git.zone/tspublish": "^1.11.6",
"@idp.global/catalog": "file:../catalog",
"@idp.global/interfaces": "file:../interfaces",
"@git.zone/tspublish": "^1.11.7",
"@idp.global/catalog": "^1.1.1",
"@idp.global/interfaces": "^1.0.1",
"@idp.global/sdk": "^1.3.0",
"@push.rocks/lik": "^6.4.1",
"@push.rocks/qenv": "^6.1.4",
"@push.rocks/smartcli": "^4.0.21",
"@push.rocks/smartcli": "^4.3.0",
"@push.rocks/smartdata": "^7.1.7",
"@push.rocks/smartdelay": "^3.1.0",
"@push.rocks/smartfile": "^13.1.3",
@@ -57,13 +58,13 @@
"argon2": "^0.44.0"
},
"devDependencies": {
"@git.zone/tsbuild": "^4.4.0",
"@git.zone/tsbundle": "^2.10.1",
"@git.zone/tsrun": "^2.0.3",
"@git.zone/tstest": "^3.6.3",
"@git.zone/tswatch": "^3.3.3",
"@git.zone/tsbuild": "^4.4.1",
"@git.zone/tsbundle": "^2.10.4",
"@git.zone/tsrun": "^2.0.4",
"@git.zone/tstest": "^3.6.6",
"@git.zone/tswatch": "^3.3.5",
"@push.rocks/projectinfo": "^5.1.0",
"@types/node": "^25.6.0"
"@types/node": "^25.9.0"
},
"private": true,
"repository": {
+565 -329
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -2,7 +2,7 @@
Identity infrastructure for apps that need accounts, sessions, organizations, invites, admin tooling, mobile passport approvals, security alerts, and OpenID Connect in one TypeScript codebase.
This repository ships the `idp.global` server, browser SDK, CLI, web UI, and tspublish submodules used by the hosted service. Shared public contracts live in the sibling `@idp.global/interfaces` package.
This repository ships the `idp.global` server, CLI, web UI, and tspublish submodules used by the hosted service. Shared public contracts live in `@idp.global/interfaces`; reusable browser/server SDK code lives in `@idp.global/sdk`.
## Issue Reporting and Security
@@ -22,10 +22,10 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
| Folder | Purpose |
| --- | --- |
| `ts/` | Backend service entrypoint and the core `Reception` managers |
| `ts_idpclient/` | Browser-focused SDK published as `@idp.global/client` |
| `ts_idpcli/` | CLI published as `@idp.global/cli` |
| `ts_web/` | Frontend bundle with login, registration, account, org, billing, and admin views |
| `../interfaces/` | Shared request and data contracts published as `@idp.global/interfaces` |
| `../sdk/` | Browser and server SDK published as `@idp.global/sdk` |
## Core Backend Pieces
@@ -142,10 +142,10 @@ The typed request surface includes:
## SDK Example
The browser SDK lives in `ts_idpclient/` and is published as `@idp.global/client`.
Browser integrations should use the dedicated SDK browser entrypoint published by `@idp.global/sdk`.
```ts
import { IdpClient } from '@idp.global/client';
import { IdpClient } from '@idp.global/sdk/browser';
const idpClient = new IdpClient('https://idp.global');
await idpClient.enableTypedSocket();
@@ -213,7 +213,7 @@ The sibling `@idp.global/interfaces` package exports the type contracts shared a
- Package manager: `pnpm`
- Main backend entrypoint: `ts/index.ts`
- Frontend entrypoint: `ts_web/index.ts`
- Browser SDK entrypoint: `ts_idpclient/index.ts`
- Browser SDK entrypoint: `@idp.global/sdk/browser`
- CLI entrypoint: `ts_idpcli/index.ts`
## License and Legal Information
@@ -18,8 +18,8 @@ As a developer, I want comprehensive documentation for the IDP client SDK so tha
- [ ] Interactive API playground/sandbox
## Technical Notes
- `ts_idpclient/` contains the client SDK
- README.md has basic usage but needs expansion
- `@idp.global/sdk` contains the browser and server SDK surfaces
- The SDK README has basic usage but needs expansion
- Generate API docs from TypeScript using TypeDoc
- Host documentation on dedicated site or GitHub pages
- Consider OpenAPI/Swagger spec for REST endpoints
+2 -2
View File
@@ -18,7 +18,7 @@ As a developer, I want to properly register my application with a unique App ID
- [ ] Delete/deactivate applications
## Technical Notes
- Current client has `id: ''` placeholder (TODO in code)
- SDK clients should receive app identity from the registered application model instead of hard-coded placeholders
- App ID is now part of the unified Apps model (`IApp` discriminated union)
- Three app types exist: Global Apps, Partner Apps, Custom OIDC Apps
- For custom applications, use the Custom OIDC Apps flow (ORG-011)
@@ -41,4 +41,4 @@ The Apps system supports three types:
- DEV-008: Submit App to AppStore
## Related TODOs
- `ts_idpclient/classes.idpclient.ts:30` - `id: '', // TODO`
- Keep app identity initialization aligned with the shared `@idp.global/sdk` client configuration.
+1 -2
View File
@@ -8,7 +8,7 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
## Scope
These stories are planning and product-discovery notes for the app repository. They are not API documentation and should be read alongside the current source in `ts/`, `ts_web/`, `ts_idpclient/`, `ts_idpcli/`, and the sibling `@idp.global/interfaces` package.
These stories are planning and product-discovery notes for the app repository. They are not API documentation and should be read alongside the current source in `ts/`, `ts_web/`, `ts_idpcli/`, the sibling `@idp.global/interfaces` package, and `@idp.global/sdk`.
## Directory Structure
@@ -97,7 +97,6 @@ Stories derived from code TODOs reference these files:
- `ts/reception/classes.jwtmanager.ts:40,52`
- `ts/reception/classes.loginsessionmanager.ts:229-238,256`
- `ts/reception/classes.billingplan.ts:16`
- `ts_idpclient/classes.idpclient.ts:30`
## License and Legal Information
-425
View File
@@ -1,425 +0,0 @@
import { IdpRequests } from './classes.idprequests.js';
import * as plugins from './plugins.js';
export class IdpClient {
// INSTANCE PRIVATE
private helpers = {
async extractDataFromJwtString(jwtString: string): Promise<plugins.idpInterfaces.data.IJwt> {
return plugins.webjwt.getDataFromJwtString(jwtString);
},
};
// INSTANCE PUBLIC
public appData: plugins.idpInterfaces.data.IAppLegacy;
public rolesReplaySubject = new plugins.smartrx.rxjs.ReplaySubject(1);
public organizationsReplaySubject = new plugins.smartrx.rxjs.ReplaySubject(1);
public parsedReceptionUrl: plugins.smarturl.Smarturl;
constructor(receptionBaseUrlArg: string, appDataArg?: plugins.idpInterfaces.data.IAppLegacy) {
if (receptionBaseUrlArg.endsWith('/')) {
receptionBaseUrlArg = receptionBaseUrlArg.slice(0, -1);
}
if (!receptionBaseUrlArg.endsWith('/typedrequest')) {
receptionBaseUrlArg = `${receptionBaseUrlArg}/typedrequest`;
}
this.parsedReceptionUrl = plugins.smarturl.Smarturl.createFromUrl(receptionBaseUrlArg);
console.log(`reception client connecting to ${this.parsedReceptionUrl.toString()}`);
if (!appDataArg) {
appDataArg = {
id: '', // TODO
appUrl: `https://${window.location.host}/`,
description: '',
logoUrl: '',
name: '',
};
}
this.appData = appDataArg;
}
public requests = new IdpRequests(this);
public checkWetherOnReceptionDomain() {
return plugins.smarturl.Smarturl.createFromUrl(window.location.href).hostname ===
this.parsedReceptionUrl.hostname;
}
/**
* app data can be transferred when redirecting to the sso domain using query params
* this message retrieves the app data when on the sso domain
*/
public async getAppDataOnSsoDomain() {
if (!window.location.href.startsWith('https://sso.workspace.global/')) {
console.error('You are trying to access SSO appData on a non sso domain.');
return null;
}
const appDataString = plugins.smarturl.Smarturl.createFromUrl(window.location.href).searchParams
.appdata;
if (!appDataString) {
console.error('no appdata query arg detected');
return null;
}
const appData = plugins.smartjson.parseBase64(appDataString);
return appData;
}
public async setJwt(jwtStringArg: string) {
await this.storeJwt(jwtStringArg);
}
public async setRefreshToken(refreshTokenArg: string) {
await this.storeRefreshToken(refreshTokenArg);
}
/**
* a typedsocket for going reactive
*/
public typedsocket!: plugins.typedsocket.TypedSocket;
/**
* a typed router to go reactive
*/
public typedrouter = new plugins.typedrequest.TypedRouter();
public statusObservable =
new plugins.smartrx.rxjs.Subject<plugins.idpInterfaces.data.TLoginStatus>();
public ssoStore = new plugins.webstore.WebStore({
storeName: 'idpglobalStore',
dbName: 'main',
});
public async storeJwt(jwtString: string) {
await this.ssoStore.set('idpJwt', jwtString);
}
public async storeRefreshToken(refreshToken: string) {
await this.ssoStore.set('idpRefreshToken', refreshToken);
}
public async getJwt(): Promise<string> {
return await this.ssoStore.get('idpJwt');
}
public async getRefreshToken(): Promise<string> {
return await this.ssoStore.get('idpRefreshToken');
}
public async getJwtData(): Promise<plugins.idpInterfaces.data.IJwt> {
return this.helpers.extractDataFromJwtString(await this.getJwt());
}
public async deleteJwt() {
await this.ssoStore.delete('idpJwt');
}
public async deleteRefreshToken() {
await this.ssoStore.delete('idpRefreshToken');
}
public async clearAuthState() {
await Promise.all([this.deleteJwt(), this.deleteRefreshToken()]);
}
/**
* performs jwt housekeeping
* only call if jwt is present
* @returns
*/
public async performJwtHousekeeping() {
let jwt = await this.getJwt();
if (!jwt) {
return null;
}
const extractedJwt = await this.helpers.extractDataFromJwtString(jwt);
if (extractedJwt.data.refreshFrom < Date.now() && Date.now() < extractedJwt.data.validUntil) {
jwt = await this.refreshJwt();
} else if (Date.now() > extractedJwt.data.validUntil) {
await this.deleteJwt();
jwt = await this.refreshJwt();
}
return jwt;
}
public async refreshJwt(refreshTokenArg?: string): Promise<string | null> {
const refreshToken = refreshTokenArg || (await this.getRefreshToken());
if (!refreshToken) {
return null;
}
await this.typedsocketDeferred.promise;
const refreshJwtReq =
this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RefreshJwt>(
'refreshJwt'
);
const response = await refreshJwtReq
.fire({
refreshToken,
})
.catch(async () => {
await this.clearAuthState();
return null;
});
if (!response?.jwt) {
await this.clearAuthState();
this.statusObservable.next(response?.status || 'loggedOut');
return null;
}
if (response.refreshToken) {
await this.storeRefreshToken(response.refreshToken);
}
await this.storeJwt(response.jwt);
this.statusObservable.next(response.status);
return response.jwt;
}
/**
* can be used to switch between pages
*/
public async getTransferToken(appDataArg?: plugins.idpInterfaces.data.IAppLegacy): Promise<string | null> {
await this.performJwtHousekeeping();
const refreshToken = await this.getRefreshToken();
if (!refreshToken) {
return null;
}
await this.typedsocketDeferred.promise;
const getTransferToken =
this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ExchangeRefreshTokenAndTransferToken>(
'exchangeRefreshTokenAndTransferToken'
);
const response = await getTransferToken.fire({
refreshToken,
appData: appDataArg || this.appData,
});
return response.transferToken;
}
/**
* gets a transfer token and switches to a location
*/
public async getTransferTokenAndSwitchToLocation(newLocationArg: string): Promise<void> {
const transferToken = await this.getTransferToken();
if (!transferToken) {
alert('failed to get transfer token!');
}
const urlInstance = plugins.smarturl.Smarturl.createFromUrl(newLocationArg, {
searchParams: {
transfertoken: transferToken,
},
});
const transferUrl = urlInstance.toString();
window.location.href = transferUrl;
return;
}
/**
* processes a transfer token
*/
public async processTransferToken(): Promise<boolean> {
const href = window.location.href;
const url = plugins.smarturl.Smarturl.createFromUrl(href);
const transferToken = url.searchParams['transfertoken'];
if (transferToken) {
await this.typedsocketDeferred.promise;
const getTransferToken =
this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ExchangeRefreshTokenAndTransferToken>(
'exchangeRefreshTokenAndTransferToken'
);
const response = await getTransferToken.fire({
transferToken,
appData: this.appData,
});
if (response.refreshToken) {
await this.refreshJwt(response.refreshToken);
} else {
globalThis.alert?.('transfer token invalid');
return false;
}
return true;
} else {
return false;
}
}
// Login Status stuff
public async checkJwtPresent() {
const jwt = await this.performJwtHousekeeping();
if (jwt) {
return true;
} else {
return false;
}
}
/**
* determines if the user is logged in
* accepts boolean to optionally require login
* @param requireLoginArg
* @returns
*/
public async determineLoginStatus(requireLoginArg: boolean = false): Promise<boolean> {
const jwtPresent = await this.checkJwtPresent();
if (jwtPresent) {
const jwt = await this.performJwtHousekeeping();
return !!jwt;
} else {
const refreshToken = await this.getRefreshToken();
if (refreshToken) {
const jwt = await this.refreshJwt(refreshToken);
if (jwt) {
return true;
}
}
const transferTokenResult = await this.processTransferToken();
if (transferTokenResult) {
// we are in the clear
return true;
} else {
if (requireLoginArg) {
const urlInstance = plugins.smarturl.Smarturl.createFromUrl(
this.parsedReceptionUrl.clone().set('path', '/login').toString(),
{
searchParams: {
appdata: plugins.smartjson.stringifyBase64(this.appData),
},
}
);
if (!globalThis.location.href.startsWith(this.parsedReceptionUrl.toString())) {
globalThis.location.href = urlInstance.toString();
}
}
return false;
}
}
}
/**
* logs out the current user
*/
public async logout() {
const idpLogoutUrl = this.parsedReceptionUrl.clone().set('path', '/logout');
const refreshToken = await this.getRefreshToken();
if (!globalThis.location.href.startsWith(idpLogoutUrl.origin)) {
// we are somewhere in an app
await this.clearAuthState();
globalThis.location.href = idpLogoutUrl.toString();
} else {
// we are in the sso page
if (!refreshToken) {
await this.clearAuthState();
window.location.href = this.parsedReceptionUrl.origin;
return;
}
await this.enableTypedSocket();
console.log(`logging out against ${this.parsedReceptionUrl.toString()}`);
const logoutTr =
this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.ILogoutRequest>(
'logout'
);
await logoutTr.fire({
refreshToken,
});
await this.clearAuthState();
const appData = await this.getAppDataOnSsoDomain();
if (appData) {
console.log(`redirecting to app after logout: ${appData.appUrl}`);
window.location.href = appData.appUrl;
} else {
console.error('no appData provided. Not redirecting after logout.');
}
if (window.location.href.startsWith(idpLogoutUrl.origin)) {
window.location.href = this.parsedReceptionUrl.origin;
}
}
}
public typedsocketDeferred = plugins.smartpromise.defer<plugins.typedsocket.TypedSocket>();
public async enableTypedSocket() {
if (this.typedsocketDeferred.claimed) {
return this.typedsocketDeferred.promise;
}
this.typedsocketDeferred.claim();
this.typedsocket = await plugins.typedsocket.TypedSocket.createClient(
this.typedrouter,
this.parsedReceptionUrl.toString()
);
this.typedsocketDeferred.resolve(this.typedsocket);
return this.typedsocketDeferred.promise;
}
public async stop() {
await this.typedsocket?.stop();
}
// ==================================
// Organization and Settings stuff
// ==================================
public async createOrganization(
orgNameArg: string,
orgSlugArg: string,
modeArg: 'checkAvailability' | 'manifest'
) {
await this.typedsocketDeferred.promise;
const validateOrg =
this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CreateOrganization>(
'createOrganization'
);
const response = await validateOrg.fire({
jwt: await this.getJwt(),
action: modeArg,
organizationName: orgNameArg,
organizationSlug: orgSlugArg,
userId: (await this.getJwtData()).id,
});
return response;
}
/**
* gets the current OrganizationRoles
*/
public async getRolesAndOrganizations() {
console.log('idpclient: getting roles and orgs...');
await this.typedsocketDeferred.promise;
const rolesAndOrganizationsForUserId =
this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetRolesAndOrganizationsForUserId>(
'getRolesAndOrganizationsForUserId'
);
const response = await rolesAndOrganizationsForUserId.fire({
jwt: await this.getJwt(),
userId: (await this.getJwtData()).id,
});
return response;
}
/**
* updates the PaddleCheckoutId for an organization.
*/
public async updatePaddleCheckoutId(orgIdArg: string, checkoutIdArg: string) {
await this.typedsocketDeferred.promise;
const updateBillingPlan =
this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdatePaymentMethod>(
'updatePaymentMethod'
);
const response = await updateBillingPlan.fire({
jwtString: await this.getJwt(),
orgId: orgIdArg,
paddle: {
checkoutId: checkoutIdArg,
},
});
return response;
}
public async whoIs() {
await this.typedsocketDeferred.promise;
const whoIs =
this.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_WhoIs>(
'whoIs'
);
const response = await whoIs.fire({
jwt: await this.getJwt(),
});
return response;
}
}
-368
View File
@@ -1,368 +0,0 @@
import * as plugins from './plugins.js';
import type { IdpClient } from "./classes.idpclient.js";
/**
* this class bundles all the typed requests that are used by the idp
* All requests use TypedSocket (WebSocket) transport
*/
export class IdpRequests {
idpClientArg: IdpClient;
constructor(idpClientArg: IdpClient) {
this.idpClientArg = idpClientArg;
}
public get afterRegistrationEmailClicked () {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_AfterRegistrationEmailClicked>(
'afterRegistrationEmailClicked'
);
}
public get setData() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_SetDataForRegistration>(
'setDataForRegistration'
);
}
public get mobileNumberVerification () {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_MobileVerificationForRegistration>(
'mobileVerificationForRegistration'
);
}
public get finishRegistration() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_FinishRegistration>(
'finishRegistration'
);
}
public get loginWithUserNameAndPassword () {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmailOrUsernameAndPassword>(
'loginWithEmailOrUsernameAndPassword'
);
}
public get obtainJwt () {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RefreshJwt>(
'refreshJwt'
);
}
public get obtainOneTimeToken () {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ExchangeRefreshTokenAndTransferToken>(
'exchangeRefreshTokenAndTransferToken'
);
}
// ============================================
// Login & Authentication
// ============================================
public get loginWithEmail() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmail>(
'loginWithEmail'
);
}
public get loginWithEmailAfterToken() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmailAfterEmailTokenAquired>(
'loginWithEmailAfterEmailTokenAquired'
);
}
public get loginWithApiToken() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_LoginWithApiToken>(
'loginWithApiToken'
);
}
public get completeOidcAuthorization() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CompleteOidcAuthorization>(
'completeOidcAuthorization'
);
}
public get prepareOidcAuthorization() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_PrepareOidcAuthorization>(
'prepareOidcAuthorization'
);
}
public get resetPassword() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ResetPassword>(
'resetPassword'
);
}
public get setNewPassword() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_SetNewPassword>(
'setNewPassword'
);
}
public get obtainDeviceId() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ObtainDeviceId>(
'obtainDeviceId'
);
}
public get attachDeviceId() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_AttachDeviceId>(
'attachDeviceId'
);
}
// ============================================
// Registration
// ============================================
public get firstRegistration() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_FirstRegistration>(
'firstRegistrationRequest'
);
}
// ============================================
// User Management
// ============================================
public get getUserData() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetUserData>(
'getUserData'
);
}
public get setUserData() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_SetUserData>(
'setUserData'
);
}
public get getUserSessions() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetUserSessions>(
'getUserSessions'
);
}
public get revokeSession() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RevokeSession>(
'revokeSession'
);
}
public get getUserActivity() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetUserActivity>(
'getUserActivity'
);
}
// ============================================
// Organization Management
// ============================================
public get getOrganizationById() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetOrganizationById>(
'getOrganizationById'
);
}
public get updateOrganization() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdateOrganization>(
'updateOrganization'
);
}
public get deleteOrganization() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_DeleteOrganization>(
'deleteOrganization'
);
}
public get getOrgRoleDefinitions() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetOrgRoleDefinitions>(
'getOrgRoleDefinitions'
);
}
public get upsertOrgRoleDefinition() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpsertOrgRoleDefinition>(
'upsertOrgRoleDefinition'
);
}
public get deleteOrgRoleDefinition() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_DeleteOrgRoleDefinition>(
'deleteOrgRoleDefinition'
);
}
// ============================================
// Member & Invitation Management
// ============================================
public get createInvitation() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CreateInvitation>(
'createInvitation'
);
}
public get getOrgInvitations() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetOrgInvitations>(
'getOrgInvitations'
);
}
public get getOrgMembers() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetOrgMembers>(
'getOrgMembers'
);
}
public get cancelInvitation() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CancelInvitation>(
'cancelInvitation'
);
}
public get resendInvitation() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_ResendInvitation>(
'resendInvitation'
);
}
public get removeMember() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RemoveMember>(
'removeMember'
);
}
public get updateMemberRoles() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdateMemberRoles>(
'updateMemberRoles'
);
}
public get transferOwnership() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_TransferOwnership>(
'transferOwnership'
);
}
public get getInvitationByToken() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetInvitationByToken>(
'getInvitationByToken'
);
}
public get acceptInvitation() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_AcceptInvitation>(
'acceptInvitation'
);
}
public get bulkCreateInvitations() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_BulkCreateInvitations>(
'bulkCreateInvitations'
);
}
public get updateAppRoleMappings() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdateAppRoleMappings>(
'updateAppRoleMappings'
);
}
// ============================================
// Billing
// ============================================
public get getBillingPlan() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetBillingPlan>(
'getBillingPlan'
);
}
public get getPaddleConfig() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetPaddleConfig>(
'getPaddleConfig'
);
}
// ============================================
// JWT Verification & Management
// ============================================
public get getPublicKeyForValidation() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetPublicKeyForValidation>(
'getPublicKeyForValidation'
);
}
public get pushPublicKeyForValidation() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_PushPublicKeyForValidation>(
'pushPublicKeyForValidation'
);
}
public get pushOrGetJwtIdBlocklist() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_PushOrGetJwtIdBlocklist>(
'pushOrGetJwtIdBlocklist'
);
}
// ============================================
// User Suspension (Admin)
// ============================================
public get suspendUser() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_SuspendUser>(
'suspendUser'
);
}
public get deleteSuspendedUser() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IDeleteSuspendedUser>(
'deleteSuspendedUser'
);
}
// ============================================
// Admin (Global Admin Only)
// ============================================
public get checkGlobalAdmin() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CheckGlobalAdmin>(
'checkGlobalAdmin'
);
}
public get getGlobalAppStats() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_GetGlobalAppStats>(
'getGlobalAppStats'
);
}
public get createGlobalApp() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_CreateGlobalApp>(
'createGlobalApp'
);
}
public get updateGlobalApp() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_UpdateGlobalApp>(
'updateGlobalApp'
);
}
public get deleteGlobalApp() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_DeleteGlobalApp>(
'deleteGlobalApp'
);
}
public get regenerateAppCredentials() {
return this.idpClientArg.typedsocket.createTypedRequest<plugins.idpInterfaces.request.IReq_RegenerateAppCredentials>(
'regenerateAppCredentials'
);
}
}
-1
View File
@@ -1 +0,0 @@
export * from './classes.idpclient.js';
-26
View File
@@ -1,26 +0,0 @@
// idp.global scope
import * as idpInterfaces from '@idp.global/interfaces';
export { idpInterfaces };
// apiglobal scope
import * as typedrequest from '@api.global/typedrequest';
import * as typedsocket from '@api.global/typedsocket';
export { typedrequest, typedsocket };
// pushrocks scope
import * as smartjson from '@push.rocks/smartjson';
import * as smartpromise from '@push.rocks/smartpromise';
import * as smartrx from '@push.rocks/smartrx';
import * as smarttime from '@push.rocks/smarttime';
import * as smarturl from '@push.rocks/smarturl';
import * as webjwt from '@push.rocks/webjwt';
import * as webstore from '@push.rocks/webstore';
export { smartjson, smartpromise, smartrx, smarttime, smarturl, webjwt, webstore };
// @tsclass scope
import * as tsclass from '@tsclass/tsclass';
export { tsclass };
-159
View File
@@ -1,159 +0,0 @@
# @idp.global/client
Browser-facing TypeScript client for talking to an `idp.global` server over `typedrequest` and `typedsocket`.
It handles login state, refresh tokens, JWT housekeeping, cross-app transfer tokens, and direct access to the typed request surface.
## Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
## Install
```bash
pnpm add @idp.global/client
```
## Quick Start
```ts
import { IdpClient } from '@idp.global/client';
const idpClient = new IdpClient('https://idp.global');
await idpClient.enableTypedSocket();
const loggedIn = await idpClient.determineLoginStatus();
if (!loggedIn) {
const loginResult = await idpClient.requests.loginWithUserNameAndPassword.fire({
username: 'user@example.com',
password: 'secret',
});
if (loginResult.refreshToken) {
await idpClient.refreshJwt(loginResult.refreshToken);
}
}
const whoIs = await idpClient.whoIs();
console.log(whoIs.user.data.email);
```
## What The Client Handles
- Normalizes the base URL to the server's `/typedrequest` endpoint.
- Stores JWT and refresh token state in a browser `WebStore`.
- Refreshes expiring JWTs via `performJwtHousekeeping()`.
- Redirects to `/login` when `determineLoginStatus(true)` is used.
- Exchanges refresh tokens for cross-app transfer tokens.
- Exposes the low-level typed requests through `idpClient.requests`.
## Common Flows
### Password Login
```ts
const result = await idpClient.requests.loginWithUserNameAndPassword.fire({
username: 'user@example.com',
password: 'secret',
});
if (result.refreshToken) {
await idpClient.refreshJwt(result.refreshToken);
}
```
### Magic Link Login
```ts
await idpClient.requests.loginWithEmail.fire({
email: 'user@example.com',
});
const result = await idpClient.requests.loginWithEmailAfterToken.fire({
email: 'user@example.com',
token: 'token-from-email',
});
await idpClient.refreshJwt(result.refreshToken);
```
### Session and Identity
```ts
await idpClient.performJwtHousekeeping();
const jwt = await idpClient.getJwt();
const jwtData = await idpClient.getJwtData();
const whoIs = await idpClient.whoIs();
console.log(jwtData.id, whoIs.user.data.username);
```
### Organizations
```ts
const rolesAndOrganizations = await idpClient.getRolesAndOrganizations();
const created = await idpClient.createOrganization(
'Acme',
'acme',
'manifest'
);
const members = await idpClient.requests.getOrgMembers.fire({
jwt: await idpClient.getJwt(),
organizationId: created.resultingOrganization.id,
});
```
### Cross-App Transfer
```ts
const transferToken = await idpClient.getTransferToken();
await idpClient.getTransferTokenAndSwitchToLocation('https://app.example.com/');
```
## Typed Request Surface
`IdpRequests` exposes typed request getters for:
- authentication
- registration
- user/session queries
- org and invitation management
- billing requests
- JWT validation key requests
- admin requests
- OIDC authorization preparation and completion
- passport device enrollment, challenge approval, alert, and push-token requests
Use these when you want full control instead of the higher-level helper methods on `IdpClient`.
## Important Runtime Notes
- The default fallback `appData` uses `window.location`, so this package is primarily browser-oriented.
- The client expects the backend `typedrequest` websocket surface to be reachable.
- Auth state is persisted in browser storage under the `idpglobalStore` store name.
- Passport, alert, and OIDC helper flows are available through `idpClient.requests` even when there is no higher-level convenience method on `IdpClient` yet.
## License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](../license) file.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
### Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
### Company Information
Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
-4
View File
@@ -1,4 +0,0 @@
{
"name": "@idp.global/client",
"order": 3
}
+25
View File
@@ -2,6 +2,10 @@
Interactive development seed tooling for local idp.global databases.
## Issue Reporting and Security
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
Run from the app repository root:
```bash
@@ -20,3 +24,24 @@ Default development admin credentials when accepted unchanged:
- Email: `admin@idp.global`
- Password: `idp.global`
## License and Legal Information
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](../license) file.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
### Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
### Company Information
Task Venture Capital GmbH
Registered at District Court Bremen HRB 35230 HB, Germany
For any legal inquiries or further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
+2 -2
View File
@@ -3,9 +3,9 @@
// project native
import * as idpCatalog from '@idp.global/catalog';
import * as idpInterfaces from '@idp.global/interfaces';
import * as leleReceptionclient from '../dist_ts_idpclient/index.js';
import * as idpClient from '@idp.global/sdk/browser';
export { idpCatalog, idpInterfaces, leleReceptionclient as idpClient };
export { idpCatalog, idpInterfaces, idpClient };
// @api.global scope
import * as typedrequest from '@api.global/typedrequest';
+2 -2
View File
@@ -2,7 +2,7 @@
The `ts_web/` folder contains the frontend for `idp.global`: login, logout, registration, account management, org management, billing, and admin UI.
It is built with `@design.estate/dees-element`, `@design.estate/dees-domtools`, and the shared `idp.global` client and interface packages.
It is built with `@design.estate/dees-element`, `@design.estate/dees-domtools`, `@idp.global/catalog`, `@idp.global/interfaces`, and `@idp.global/sdk/browser`.
## Issue Reporting and Security
@@ -59,7 +59,7 @@ pnpm watch
## Notes
- The app metadata in `ts_web/index.ts` identifies the site as `idp.global`.
- The frontend uses the shared client package for auth state and backend communication.
- The frontend uses `@idp.global/sdk/browser` for auth state and backend communication.
- Account-related UI is split into reusable elements plus state containers in `states/`.
- The router treats `/account{/*path}` as the signed-in account area, so account subroutes can stay in the SPA shell.