8 Commits

Author SHA1 Message Date
philkunz 2c0e771da2 1.2.2
Docker (tags) / security (push) Failing after 0s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-10-04 15:43:37 +02:00
philkunz 4deaafc3a2 fix(core): Update dependencies and refactor registration process 2024-10-04 15:43:36 +02:00
philkunz 629bf19845 1.2.1
Docker (tags) / security (push) Failing after 0s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-10-04 02:18:48 +02:00
philkunz 9e2d45123f fix(core): Added logging for user email login process and fixed client URL parsing 2024-10-04 02:18:47 +02:00
philkunz 833b5e0a84 1.2.0
Docker (tags) / security (push) Failing after 0s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-10-01 20:03:50 +02:00
philkunz e36b701812 feat(web): Improve UI styling and add registration prompt 2024-10-01 20:03:49 +02:00
philkunz 8d4bfe6e3a 1.1.1
Docker (tags) / security (push) Failing after 0s
Docker (tags) / test (push) Has been skipped
Docker (tags) / release (push) Has been skipped
Docker (tags) / metadata (push) Has been skipped
2024-10-01 13:49:19 +02:00
philkunz 9f9c543365 fix(core): Corrected typos and added missing keywords. 2024-10-01 13:49:18 +02:00
49 changed files with 2115 additions and 921 deletions
+29
View File
@@ -1,5 +1,34 @@
# Changelog # Changelog
## 2024-10-04 - 1.2.2 - fix(core)
Update dependencies and refactor registration process
- Updated @design.estate/dees-catalog, @design.estate/dees-domtools, and @design.estate/dees-element dependencies to their latest versions.
- Refactored registration process to improve validation flow.
- Improved user interface for login and registration prompts.
- Fixed issues with email and token validation during registration.
## 2024-10-04 - 1.2.1 - fix(core)
Added logging for user email login process and fixed client URL parsing
- Added info logging when loginWithEmail is requested and when a user is found.
- Ensured reception client parses the URL correctly in IdpClient and IdpRequests classes.
- Updated login process flow in idp-logincontainer and idp-loginprompt elements.
- Improved element loading mechanism with updated state management in viewcontainer.
## 2024-10-01 - 1.2.0 - feat(web)
Improve UI styling and add registration prompt
- Updated max-width of login container to improve layout consistency
- Added new component for user registration
- Improved styling for various elements including buttons and text boxes
## 2024-10-01 - 1.1.1 - fix(core)
Corrected typos and added missing keywords.
- Added missing newline at the end of package.json.
- Revised various typos and added missing keywords.
## 2024-09-29 - 1.1.0 - feat(web) ## 2024-09-29 - 1.1.0 - feat(web)
Implement view container and update elements Implement view container and update elements
+7 -2
View File
@@ -18,13 +18,18 @@
"JWT", "JWT",
"TypeScript", "TypeScript",
"user login", "user login",
"user registration",
"session handling", "session handling",
"email verification", "email verification",
"mobile verification", "mobile verification",
"user roles", "user roles",
"organization management", "organization management",
"billing management" "billing management",
"password reset",
"two-factor authentication",
"OAuth",
"API",
"user data",
"user sessions"
] ]
} }
}, },
+15 -10
View File
@@ -1,6 +1,6 @@
{ {
"name": "@idp.global/idp.global", "name": "@idp.global/idp.global",
"version": "1.1.0", "version": "1.2.2",
"description": "An identity provider software managing user authentications, registrations, and sessions.", "description": "An identity provider software managing user authentications, registrations, and sessions.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
"typings": "dist_ts/index.d.ts", "typings": "dist_ts/index.d.ts",
@@ -21,9 +21,9 @@
"@api.global/typedserver": "^3.0.51", "@api.global/typedserver": "^3.0.51",
"@api.global/typedsocket": "^3.0.1", "@api.global/typedsocket": "^3.0.1",
"@consentsoftware_private/catalog": "^1.0.73", "@consentsoftware_private/catalog": "^1.0.73",
"@design.estate/dees-catalog": "^1.1.8", "@design.estate/dees-catalog": "^1.1.10",
"@design.estate/dees-domtools": "^2.0.23", "@design.estate/dees-domtools": "^2.0.61",
"@design.estate/dees-element": "^2.0.15", "@design.estate/dees-element": "^2.0.39",
"@push.rocks/lik": "^6.0.15", "@push.rocks/lik": "^6.0.15",
"@push.rocks/qenv": "^6.0.5", "@push.rocks/qenv": "^6.0.5",
"@push.rocks/smartdata": "^5.2.10", "@push.rocks/smartdata": "^5.2.10",
@@ -36,15 +36,15 @@
"@push.rocks/smartpath": "^5.0.5", "@push.rocks/smartpath": "^5.0.5",
"@push.rocks/smartpromise": "^4.0.4", "@push.rocks/smartpromise": "^4.0.4",
"@push.rocks/smartrx": "^3.0.7", "@push.rocks/smartrx": "^3.0.7",
"@push.rocks/smartstate": "^2.0.0", "@push.rocks/smartstate": "^2.0.19",
"@push.rocks/smarttime": "^4.0.8", "@push.rocks/smarttime": "^4.0.8",
"@push.rocks/smartunique": "^3.0.9", "@push.rocks/smartunique": "^3.0.9",
"@push.rocks/smarturl": "^3.0.7", "@push.rocks/smarturl": "^3.1.0",
"@push.rocks/taskbuffer": "^3.1.7", "@push.rocks/taskbuffer": "^3.1.7",
"@push.rocks/webjwt": "^1.0.9", "@push.rocks/webjwt": "^1.0.9",
"@push.rocks/websetup": "^3.0.15", "@push.rocks/websetup": "^3.0.15",
"@push.rocks/webstore": "^2.0.20", "@push.rocks/webstore": "^2.0.20",
"@serve.zone/platformclient": "^1.0.6", "@serve.zone/platformclient": "^1.1.0",
"@tsclass/tsclass": "^4.1.2", "@tsclass/tsclass": "^4.1.2",
"@uptime.link/webwidget": "^1.1.2" "@uptime.link/webwidget": "^1.1.2"
}, },
@@ -54,7 +54,7 @@
"@git.zone/tsrun": "^1.2.8", "@git.zone/tsrun": "^1.2.8",
"@git.zone/tswatch": "^2.0.1", "@git.zone/tswatch": "^2.0.1",
"@push.rocks/projectinfo": "^5.0.1", "@push.rocks/projectinfo": "^5.0.1",
"@types/node": "^22.7.2" "@types/node": "^22.7.4"
}, },
"private": true, "private": true,
"repository": { "repository": {
@@ -89,12 +89,17 @@
"JWT", "JWT",
"TypeScript", "TypeScript",
"user login", "user login",
"user registration",
"session handling", "session handling",
"email verification", "email verification",
"mobile verification", "mobile verification",
"user roles", "user roles",
"organization management", "organization management",
"billing management" "billing management",
"password reset",
"two-factor authentication",
"OAuth",
"API",
"user data",
"user sessions"
] ]
} }
+1218 -122
View File
File diff suppressed because it is too large Load Diff
+9 -9
View File
@@ -27,7 +27,7 @@ import * as domtools from '@design.estate/dees-domtools';
import { html, render } from '@design.estate/dees-element'; import { html, render } from '@design.estate/dees-element';
import { IdpWelcome } from './elements/idp-welcome.js'; import { IdpWelcome } from './elements/idp-welcome.js';
// Define asynchronous run function // Define an asynchronous run function
const run = async () => { const run = async () => {
// Set up DOM tools // Set up DOM tools
const domtoolsInstance = await domtools.DomTools.setupDomTools(); const domtoolsInstance = await domtools.DomTools.setupDomTools();
@@ -56,10 +56,10 @@ const run = async () => {
}, },
}); });
// Set up service worker // Set up the service worker
const serviceWorker = await serviceworker.getServiceworkerClient(); const serviceWorker = await serviceworker.getServiceworkerClient();
// Render main template // Render the main template
const mainTemplate = html` const mainTemplate = html`
<style> <style>
body { body {
@@ -79,7 +79,7 @@ run();
### Using the IDP Client ### Using the IDP Client
The IDP Client is essential to communicate with the IDP server. Below is a sample on how to set up and use the IDP client: The IDP Client is essential to communicate with the IDP server. Below is a sample of how to set up and use the IDP client:
```typescript ```typescript
import { IdpState } from './idp.state.js'; import { IdpState } from './idp.state.js';
@@ -99,7 +99,7 @@ export class IdpDemo {
username: 'user@example.com', username: 'user@example.com',
password: 'password123', password: 'password123',
}); });
if(response.refreshToken) { if (response.refreshToken) {
await idpClient.storeJwt(response.jwt); await idpClient.storeJwt(response.jwt);
console.log("Logged in successfully, JWT stored."); console.log("Logged in successfully, JWT stored.");
} else { } else {
@@ -176,7 +176,7 @@ export class IdpRegistrationStepper extends plugins.DeesElement {
last_name: formData.LastName, last_name: formData.LastName,
}, },
}); });
// Proceed to next steps as per the registration flow // Proceed to the next steps as per the registration flow
} }
private renderErrorMessage(message: string) { private renderErrorMessage(message: string) {
@@ -219,7 +219,7 @@ export class OrganizationManager {
organizationSlug: slug, organizationSlug: slug,
action: 'manifest', action: 'manifest',
}); });
if(response.resultingOrganization) { if (response.resultingOrganization) {
console.log(`Organization ${name} created successfully.`); console.log(`Organization ${name} created successfully.`);
} else { } else {
console.log(`Organization creation failed.`); console.log(`Organization creation failed.`);
@@ -247,7 +247,7 @@ export const refreshJwt = async (client: IdpClient) => {
const response = await client.requests.refreshJwt.fire({ const response = await client.requests.refreshJwt.fire({
refreshToken: currentJwt.data.refreshToken refreshToken: currentJwt.data.refreshToken
}); });
if(response.jwt) { if (response.jwt) {
await client.storeJwt(response.jwt); await client.storeJwt(response.jwt);
console.log("JWT refreshed and stored."); console.log("JWT refreshed and stored.");
return response.jwt; return response.jwt;
@@ -290,7 +290,7 @@ const idpClient = new IdpClient('https://reception.lossless.one/typedrequest');
getTransferToken(idpClient); getTransferToken(idpClient);
``` ```
This comprehensive guide should help you with a detailed understanding of setting up and using the `@idp.global/idp.global` module effectively. This comprehensive guide should help you understand the detailed setup and usage of the `@idp.global/idp.global` module effectively.
## License and Legal Information ## License and Legal Information
+1 -1
View File
@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@idp.global/idp.global', name: '@idp.global/idp.global',
version: '1.1.0', version: '1.2.2',
description: 'An identity provider software managing user authentications, registrations, and sessions.' description: 'An identity provider software managing user authentications, registrations, and sessions.'
} }
-14
View File
@@ -1,14 +0,0 @@
// native scope
import * as path from 'path';
export { path };
// @api.global scope
import * as typedserver from '@api.global/typedserver';
export { typedserver };
// @pushrocks scope
import * as qenv from '@push.rocks/qenv';
import * as smartpath from '@push.rocks/smartpath';
export { qenv, smartpath };
+18 -2
View File
@@ -1,5 +1,6 @@
import * as plugins from './ffb.plugins.js'; import * as plugins from './plugins.js';
import * as paths from './ffb.paths.js'; import * as paths from './paths.js';
import { Reception } from './reception/classes.reception.js';
export const runCli = async () => { export const runCli = async () => {
const serviceQenv = new plugins.qenv.Qenv('./', './.nogit', false); const serviceQenv = new plugins.qenv.Qenv('./', './.nogit', false);
@@ -8,5 +9,20 @@ export const runCli = async () => {
domain: 'idp.global', domain: 'idp.global',
serveDir: paths.distWebDir, serveDir: paths.distWebDir,
}); });
// lets add the reception routes
const reception = new Reception({
name: (await serviceQenv.getEnvVarOnDemand('INSTANCE_NAME')) || 'idp.global',
mongoDescriptor: {
mongoDbUser: await serviceQenv.getEnvVarOnDemand('MONGO_DB_USER'),
mongoDbName: await serviceQenv.getEnvVarOnDemand('MONGO_DB_NAME'),
mongoDbPass: await serviceQenv.getEnvVarOnDemand('MONGO_DB_PASS'),
mongoDbUrl: await serviceQenv.getEnvVarOnDemand('MONGO_DB_URL'),
},
websiteServer: websiteServer,
baseUrl: await serviceQenv.getEnvVarOnDemand('IDP_BASEURL'),
});
await reception.start();
await websiteServer.start(); await websiteServer.start();
}; };
+1 -1
View File
@@ -1,4 +1,4 @@
import * as plugins from './ffb.plugins.js'; import * as plugins from './plugins.js';
export const packageDir = plugins.path.join( export const packageDir = plugins.path.join(
plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url),
+12 -14
View File
@@ -1,34 +1,32 @@
// node native // Native scope
import * as path from 'path'; import * as path from 'path';
export { path }; export { path };
// project scope // Project scope
import * as lointReception from '../../dist_ts_interfaces/index.js'; import * as lointReception from '../dist_ts_interfaces/index.js';
export { lointReception };
export { lointReception, }; // @api.global scope
import * as typedserver from '@api.global/typedserver';
// @apiglobal scope
import * as typedrequest from '@api.global/typedrequest'; import * as typedrequest from '@api.global/typedrequest';
import * as typedsocket from '@api.global/typedsocket'; import * as typedsocket from '@api.global/typedsocket';
export { typedrequest, typedsocket }; export { typedserver, typedrequest, typedsocket };
// @serve.zone scope // @serve.zone scope
import * as szPlatformClient from '@serve.zone/platformclient'; import * as szPlatformClient from '@serve.zone/platformclient';
export { szPlatformClient }; export { szPlatformClient };
// @push.rocks scope
// @pushrocks scope
import * as lik from '@push.rocks/lik'; import * as lik from '@push.rocks/lik';
import * as projectinfo from '@push.rocks/projectinfo'; import * as projectinfo from '@push.rocks/projectinfo';
import * as qenv from '@push.rocks/qenv'; import * as qenv from '@push.rocks/qenv';
import * as smartdata from '@push.rocks/smartdata'; import * as smartdata from '@push.rocks/smartdata';
import * as smartdelay from '@push.rocks/smartdelay'; import * as smartdelay from '@push.rocks/smartdelay';
import * as smartjwt from '@push.rocks/smartjwt';
import * as smartlog from '@push.rocks/smartlog';
import * as smartmail from '@push.rocks/smartmail'; import * as smartmail from '@push.rocks/smartmail';
import * as smarthash from '@push.rocks/smarthash'; import * as smarthash from '@push.rocks/smarthash';
import * as smartjwt from '@push.rocks/smartjwt';
import * as smartpath from '@push.rocks/smartpath'; import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise'; import * as smartpromise from '@push.rocks/smartpromise';
import * as smarttime from '@push.rocks/smarttime'; import * as smarttime from '@push.rocks/smarttime';
@@ -41,9 +39,10 @@ export {
qenv, qenv,
smartdata, smartdata,
smartdelay, smartdelay,
smartmail,
smarthash, smarthash,
smartjwt, smartjwt,
smartlog,
smartmail,
smartpath, smartpath,
smartpromise, smartpromise,
smarttime, smarttime,
@@ -53,5 +52,4 @@ export {
// @tsclass scope // @tsclass scope
import * as tsclass from '@tsclass/tsclass'; import * as tsclass from '@tsclass/tsclass';
export { tsclass }; export { tsclass };
+1 -1
View File
@@ -1,5 +1,5 @@
import { ApiTokenManager } from './classes.apitokenmanager.js'; import { ApiTokenManager } from './classes.apitokenmanager.js';
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
@plugins.smartdata.Manager(() => { @plugins.smartdata.Manager(() => {
return (this as any).manager; return (this as any).manager;
+1 -1
View File
@@ -1,5 +1,5 @@
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
export class ApiTokenManager { export class ApiTokenManager {
public receptionRef: Reception; public receptionRef: Reception;
+1 -1
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { BillingPlanManager } from './classes.billingplanmanager.js'; import { BillingPlanManager } from './classes.billingplanmanager.js';
import { User } from './classes.user.js'; import { User } from './classes.user.js';
+1 -1
View File
@@ -1,6 +1,6 @@
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
import { BillingPlan } from './classes.billingplan.js'; import { BillingPlan } from './classes.billingplan.js';
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
export class BillingPlanManager { export class BillingPlanManager {
public receptionRef: Reception; public receptionRef: Reception;
+1 -1
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
import { logger } from './logging.js'; import { logger } from './logging.js';
+1 -1
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { JwtManager } from './classes.jwtmanager.js'; import { JwtManager } from './classes.jwtmanager.js';
/** /**
+5 -5
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
import { Jwt } from './classes.jwt.js'; import { Jwt } from './classes.jwt.js';
@@ -60,7 +60,7 @@ export class JwtManager {
public async pushPublicKeyToClients() { public async pushPublicKeyToClients() {
const targetConnections = const targetConnections =
await this.receptionRef.serviceServer.typedsocket.findAllTargetConnectionsByTag<plugins.lointReception.tags.ITag_LolePubapi>( await this.receptionRef.options.websiteServer.typedserver.typedsocket.findAllTargetConnectionsByTag<plugins.lointReception.tags.ITag_LolePubapi>(
'lole-reception', 'lole-reception',
{ {
backendToken: '', backendToken: '',
@@ -68,7 +68,7 @@ export class JwtManager {
); );
for (const targetConnection of targetConnections) { for (const targetConnection of targetConnections) {
const pushPublicKeyTr = const pushPublicKeyTr =
this.receptionRef.serviceServer.typedsocket.createTypedRequest<plugins.lointReception.request.IReq_PushPublicKeyForValidation>( this.receptionRef.options.websiteServer.typedserver.typedsocket.createTypedRequest<plugins.lointReception.request.IReq_PushPublicKeyForValidation>(
'pushPublicKeyForValidation', 'pushPublicKeyForValidation',
targetConnection targetConnection
); );
@@ -80,7 +80,7 @@ export class JwtManager {
public async pushBlockedJwtIdListToClients() { public async pushBlockedJwtIdListToClients() {
const targetConnections = const targetConnections =
await this.receptionRef.serviceServer.typedsocket.findAllTargetConnectionsByTag<plugins.lointReception.tags.ITag_LolePubapi>( await this.receptionRef.options.websiteServer.typedserver.typedsocket.findAllTargetConnectionsByTag<plugins.lointReception.tags.ITag_LolePubapi>(
'lole-reception', 'lole-reception',
{ {
backendToken: '', backendToken: '',
@@ -88,7 +88,7 @@ export class JwtManager {
); );
for (const targetConnection of targetConnections) { for (const targetConnection of targetConnections) {
const pushPublicKeyTr = const pushPublicKeyTr =
this.receptionRef.serviceServer.typedsocket.createTypedRequest<plugins.lointReception.request.IReq_PushOrGetJwtIdBlocklist>( this.receptionRef.options.websiteServer.typedserver.typedsocket.createTypedRequest<plugins.lointReception.request.IReq_PushOrGetJwtIdBlocklist>(
'pushOrGetJwtIdBlocklist', 'pushOrGetJwtIdBlocklist',
targetConnection targetConnection
); );
+1 -1
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { LoginSessionManager } from './classes.loginsessionmanager.js'; import { LoginSessionManager } from './classes.loginsessionmanager.js';
import { User } from './classes.user.js'; import { User } from './classes.user.js';
+6 -1
View File
@@ -1,6 +1,7 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { LoginSession } from './classes.loginsession.js'; import { LoginSession } from './classes.loginsession.js';
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
import { logger } from './logging.js';
export class LoginSessionManager { export class LoginSessionManager {
// refs // refs
@@ -81,12 +82,14 @@ export class LoginSessionManager {
new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_LoginWithEmail>( new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_LoginWithEmail>(
'loginWithEmail', 'loginWithEmail',
async (requestDataArg) => { async (requestDataArg) => {
logger.log('info', `loginWithEmail requested for: ${requestDataArg.email}`);
const existingUser = await this.receptionRef.userManager.CUser.getInstance({ const existingUser = await this.receptionRef.userManager.CUser.getInstance({
data: { data: {
email: requestDataArg.email, email: requestDataArg.email,
}, },
}); });
if (existingUser) { if (existingUser) {
logger.log('info', `loginWithEmail found user: ${existingUser.data.email}`);
this.emailTokenMap.findOneAndRemoveSync( this.emailTokenMap.findOneAndRemoveSync(
(itemArg) => itemArg.email === existingUser.data.email (itemArg) => itemArg.email === existingUser.data.email
); );
@@ -103,6 +106,8 @@ export class LoginSessionManager {
); );
}); });
this.receptionRef.receptionMailer.sendLoginWithEMailMail(existingUser, loginEmailToken); this.receptionRef.receptionMailer.sendLoginWithEMailMail(existingUser, loginEmailToken);
} else {
logger.log('info', `loginWithEmail did not find user: ${requestDataArg.email}`);
} }
return { return {
status: 'ok', status: 'ok',
+1 -1
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { OrganizationManager } from './classes.organizationmanager.js'; import { OrganizationManager } from './classes.organizationmanager.js';
import { User } from './classes.user.js'; import { User } from './classes.user.js';
+1 -1
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
import { Organization } from './classes.organization.js'; import { Organization } from './classes.organization.js';
import { User } from './classes.user.js'; import { User } from './classes.user.js';
+25 -10
View File
@@ -1,11 +1,10 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import * as paths from './paths.js'; import * as paths from '../paths.js';
import { logger } from './logging.js'; import { logger } from './logging.js';
import { JwtManager } from './classes.jwtmanager.js'; import { JwtManager } from './classes.jwtmanager.js';
import { LoginSessionManager } from './classes.loginsessionmanager.js'; import { LoginSessionManager } from './classes.loginsessionmanager.js';
import { RegistrationSessionManager } from './classes.registrationsessionmanager.js'; import { RegistrationSessionManager } from './classes.registrationsessionmanager.js';
import { ReceptionServer } from './classes.receptionserver.js';
import { ReceptionDb } from './classes.receptiondb.js'; import { ReceptionDb } from './classes.receptiondb.js';
import { ReceptionMailer } from './classes.receptionmailer.js'; import { ReceptionMailer } from './classes.receptionmailer.js';
import { UserManager } from './classes.usermanager.js'; import { UserManager } from './classes.usermanager.js';
@@ -15,6 +14,16 @@ import { OrganizationManager } from './classes.organizationmanager.js';
import { RoleManager } from './classes.rolemanager.js'; import { RoleManager } from './classes.rolemanager.js';
import { BillingPlanManager } from './classes.billingplanmanager.js'; import { BillingPlanManager } from './classes.billingplanmanager.js';
export interface IReceptionOptions {
/**
* a name for the idp instance.
*/
name: string;
mongoDescriptor: plugins.smartdata.IMongoDescriptor;
websiteServer: plugins.typedserver.utilityservers.UtilityWebsiteServer;
baseUrl: string;
}
export class Reception { export class Reception {
public projectinfoNpm = new plugins.projectinfo.ProjectinfoNpm(paths.packageDir); public projectinfoNpm = new plugins.projectinfo.ProjectinfoNpm(paths.packageDir);
public typedrouter = new plugins.typedrequest.TypedRouter(); public typedrouter = new plugins.typedrequest.TypedRouter();
@@ -22,9 +31,6 @@ export class Reception {
public szPlatformClient = new plugins.szPlatformClient.SzPlatformClient(); public szPlatformClient = new plugins.szPlatformClient.SzPlatformClient();
public db = new ReceptionDb(this); public db = new ReceptionDb(this);
// server
public serviceServer = new ReceptionServer(this);
// managers // managers
public jwtManager = new JwtManager(this); public jwtManager = new JwtManager(this);
public loginSessionManager = new LoginSessionManager(this); public loginSessionManager = new LoginSessionManager(this);
@@ -37,16 +43,26 @@ export class Reception {
public billingPlanManager = new BillingPlanManager(this); public billingPlanManager = new BillingPlanManager(this);
housekeeping = new ReceptionHousekeeping(this); housekeeping = new ReceptionHousekeeping(this);
constructor(public databaseName?: string) {} constructor(public options: IReceptionOptions) {
if (!options.mongoDescriptor) {
throw new Error('mongoDescriptor is required');
}
if (!options.websiteServer) {
throw new Error('websiteServer is required');
}
}
/** /**
* starts the reception instance * starts the reception instance
*/ */
public async start() { public async start() {
await this.szPlatformClient.init(await this.serviceQenv.getEnvVarOnDemand('SERVEZONE_PLATFROM_AUTHORIZATION'));
logger.log('info', 'starting reception'); logger.log('info', 'starting reception');
await this.db.start(this.databaseName); logger.log('info', 'adding typedrouter to website server');
this.options.websiteServer.typedrouter.addTypedRouter(this.typedrouter);
logger.log('info', 'starting database');
await this.db.start();
await this.jwtManager.start(); await this.jwtManager.start();
await this.serviceServer.start();
} }
/** /**
@@ -54,7 +70,6 @@ export class Reception {
*/ */
public async stop() { public async stop() {
await this.housekeeping.stop(); await this.housekeeping.stop();
await this.serviceServer.stop();
console.log('stopped serviceserver!'); console.log('stopped serviceserver!');
await this.db.stop(); await this.db.stop();
} }
+4 -8
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
export class ReceptionDb { export class ReceptionDb {
@@ -9,13 +9,9 @@ export class ReceptionDb {
this.receptionRef = receptionRefArg; this.receptionRef = receptionRefArg;
} }
public async start(databaseNameArg?: string) { public async start() {
this.smartdataDb = new plugins.smartdata.SmartdataDb({ console.log(this.receptionRef.options.mongoDescriptor);
mongoDbUser: await this.receptionRef.serviceQenv.getEnvVarOnDemand('MONGO_DB_USER'), this.smartdataDb = new plugins.smartdata.SmartdataDb(this.receptionRef.options.mongoDescriptor);
mongoDbName: databaseNameArg || await this.receptionRef.serviceQenv.getEnvVarOnDemand('MONGO_DB_NAME'),
mongoDbPass: await this.receptionRef.serviceQenv.getEnvVarOnDemand('MONGO_DB_PASS'),
mongoDbUrl: await this.receptionRef.serviceQenv.getEnvVarOnDemand('MONGO_DB_URL'),
});
await this.smartdataDb.init(); await this.smartdataDb.init();
} }
+5 -4
View File
@@ -1,7 +1,7 @@
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
import { RegistrationSession } from './classes.registrationsession.js'; import { RegistrationSession } from './classes.registrationsession.js';
import { User } from './classes.user.js'; import { User } from './classes.user.js';
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
export class ReceptionMailer { export class ReceptionMailer {
public receptionRef: Reception; public receptionRef: Reception;
@@ -152,9 +152,9 @@ export class ReceptionMailer {
</html> </html>
`; `;
public sendRegistrationEmail(signupSessionArg: RegistrationSession, validationTokenArg: string) { public async sendRegistrationEmail(signupSessionArg: RegistrationSession, validationTokenArg: string) {
this.receptionRef.szPlatformClient.emailConnector.sendEmail({ this.receptionRef.szPlatformClient.emailConnector.sendEmail({
from: 'workspace.global <noreply@mail.workspace.global>', from: `idp.global@${this.receptionRef.options.baseUrl} <noreply@mail.workspace.global>`,
title: 'Verify your Email Address!', title: 'Verify your Email Address!',
to: signupSessionArg.emailAddress, to: signupSessionArg.emailAddress,
body: this.createBodyString(` body: this.createBodyString(`
@@ -163,7 +163,7 @@ export class ReceptionMailer {
}">${signupSessionArg.emailAddress}</a></h1> }">${signupSessionArg.emailAddress}</a></h1>
<p>It looks like you requested to register an account with us. We just want to make sure it really was you.</p> <p>It looks like you requested to register an account with us. We just want to make sure it really was you.</p>
<p>In case it was you, <b>please continue with the registration process by clicking the button below</b>. Otherwise, please ignore this email.</p> <p>In case it was you, <b>please continue with the registration process by clicking the button below</b>. Otherwise, please ignore this email.</p>
<a href="https://registration.workspace.global/finishregistration?validationtoken=${encodeURI( <a href="${this.receptionRef.options.baseUrl}/finishregistration?validationtoken=${encodeURI(
validationTokenArg validationTokenArg
)}"><div class="button"> )}"><div class="button">
continue with registration continue with registration
@@ -229,6 +229,7 @@ export class ReceptionMailer {
} }
public sendLoginWithEMailMail(userArg: User, validationTokenArg: string) { public sendLoginWithEMailMail(userArg: User, validationTokenArg: string) {
console.log(`sending login email to ${userArg.data.email}`);
this.receptionRef.szPlatformClient.emailConnector.sendEmail({ this.receptionRef.szPlatformClient.emailConnector.sendEmail({
from: 'workspace.global <noreply@mail.workspace.global>', from: 'workspace.global <noreply@mail.workspace.global>',
title: 'Click to login!', title: 'Click to login!',
-35
View File
@@ -1,35 +0,0 @@
import * as plugins from './plugins.js';
import { Reception } from './classes.reception.js';
export class ReceptionServer {
public receptionRef: Reception;
public serviceServer: plugins.loleServiceServer.ServiceServer;
public typedsocket: plugins.typedsocket.TypedSocket;
constructor(receptionRef: Reception) {
this.receptionRef = receptionRef;
this.serviceServer = new plugins.loleServiceServer.ServiceServer({
serviceDomain: 'reception.lossless.one',
serviceName: 'reception',
serviceVersion: this.receptionRef.projectinfoNpm.version,
port: parseInt(this.receptionRef.serviceQenv.getEnvVarOnDemand('TEST_PORT')) || 3000,
addCustomRoutes: async (serverArg) => {
serverArg.addRoute(
'/typedrequest',
new plugins.loleServiceServer.HandlerTypedRouter(this.receptionRef.typedrouter)
);
},
});
}
async start() {
await this.serviceServer.start();
this.typedsocket = this.serviceServer.typedServer.typedsocket;
this.serviceServer.typedServer.typedrouter.addTypedRouter(this.receptionRef.typedrouter);
}
async stop() {
await this.typedsocket.stop();
await this.serviceServer.stop();
}
}
+1 -1
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
/** /**
* can be used to store binary data for users and organizations * can be used to store binary data for users and organizations
+8 -13
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { RegistrationSessionManager } from './classes.registrationsessionmanager.js'; import { RegistrationSessionManager } from './classes.registrationsessionmanager.js';
import { logger } from './logging.js'; import { logger } from './logging.js';
@@ -157,18 +157,13 @@ export class RegistrationSession {
* validate the mobile number of someone * validate the mobile number of someone
*/ */
public async sendValidationSms() { public async sendValidationSms() {
if (!process.env.TEST_MODE) { this.smsCode =
this.smsCode = await this.registrationSessionManagerRef.receptionRef.szPlatformClient.smsConnector.sendSmsVerifcation(
await this.registrationSessionManagerRef.receptionRef.loleSmsClientInstance.sendSmsVerifcation( {
{ fromName: this.registrationSessionManagerRef.receptionRef.options.name,
fromName: 'w...global', toNumber: parseInt(this.collectedData.userData.mobileNumber),
toNumber: parseInt(this.collectedData.userData.mobileNumber), }
} );
);
} else {
console.log('Not sending SMS in automated test mode');
this.smsCode = '123456';
}
} }
/** /**
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { RegistrationSession } from './classes.registrationsession.js'; import { RegistrationSession } from './classes.registrationsession.js';
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
import { logger } from './logging.js'; import { logger } from './logging.js';
@@ -63,6 +63,7 @@ export class RegistrationSessionManager {
new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_AfterRegistrationEmailClicked>( new plugins.typedrequest.TypedHandler<plugins.lointReception.request.IReq_AfterRegistrationEmailClicked>(
'afterRegistrationEmailClicked', 'afterRegistrationEmailClicked',
async (requestData) => { async (requestData) => {
console.log(requestData);
const signupSession = await this.registrationSessions.find(async (itemArg) => const signupSession = await this.registrationSessions.find(async (itemArg) =>
itemArg.validateEmailToken(requestData.token) itemArg.validateEmailToken(requestData.token)
); );
+1 -1
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
@plugins.smartdata.Manager() @plugins.smartdata.Manager()
export class Role extends plugins.smartdata.SmartDataDbDoc< export class Role extends plugins.smartdata.SmartDataDbDoc<
+1 -1
View File
@@ -2,7 +2,7 @@ import { Organization } from './classes.organization.js';
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
import { Role } from './classes.role.js'; import { Role } from './classes.role.js';
import { User } from './classes.user.js'; import { User } from './classes.user.js';
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
export class RoleManager { export class RoleManager {
// INSTANCE // INSTANCE
+1 -1
View File
@@ -1,4 +1,4 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import { UserManager } from './classes.usermanager.js'; import { UserManager } from './classes.usermanager.js';
/** /**
+1 -1
View File
@@ -1,6 +1,6 @@
import { Reception } from './classes.reception.js'; import { Reception } from './classes.reception.js';
import { User } from './classes.user.js'; import { User } from './classes.user.js';
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
/** /**
* a user manager * a user manager
-13
View File
@@ -1,15 +1,2 @@
// general exports for testing // general exports for testing
export * from './classes.reception.js'; export * from './classes.reception.js';
// running it in production
import { Reception } from './classes.reception.js';
let reception: Reception;
export const runCli = async () => {
reception = new Reception();
await reception.start();
};
export const stop = async () => {
await reception.stop();
};
+3 -10
View File
@@ -1,13 +1,6 @@
import * as plugins from './plugins.js'; import * as plugins from '../plugins.js';
import * as paths from './paths.js'; import * as paths from '../paths.js';
const projectinfoNpm = new plugins.projectinfo.ProjectinfoNpm(paths.packageDir); const projectinfoNpm = new plugins.projectinfo.ProjectinfoNpm(paths.packageDir);
export const logger = plugins.loleLog.createLoleLogger({ export const logger = new plugins.smartlog.ConsoleLog();
companyUnit: 'Lossless Cloud',
containerName: 'reception',
containerVersion: projectinfoNpm.version,
sentryAppName: 'reception',
sentryDsn: 'https://fd929bdcad0a41c0b7853cdea04f9c96@o169278.ingest.sentry.io/5272722',
zone: 'servezone',
});
-3
View File
@@ -1,3 +0,0 @@
import * as plugins from './plugins.js';
export const packageDir = plugins.path.join(plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), '../');
+19 -15
View File
@@ -15,16 +15,16 @@ export class IdpClient {
public rolesReplaySubject = new plugins.smartrx.rxjs.ReplaySubject(1); public rolesReplaySubject = new plugins.smartrx.rxjs.ReplaySubject(1);
public organizationsReplaySubject = new plugins.smartrx.rxjs.ReplaySubject(1); public organizationsReplaySubject = new plugins.smartrx.rxjs.ReplaySubject(1);
public receptionTrUrl: string; public parsedReceptionUrl: plugins.smarturl.Smarturl;
constructor(receptionBaseUrlArg: string, appDataArg?: plugins.lointReception.data.IApp) { constructor(receptionBaseUrlArg: string, appDataArg?: plugins.lointReception.data.IApp) {
this.receptionTrUrl = receptionBaseUrlArg if (receptionBaseUrlArg.endsWith('/')) {
if (this.receptionTrUrl.endsWith('/')) { receptionBaseUrlArg = receptionBaseUrlArg.slice(0, -1);
this.receptionTrUrl = this.receptionTrUrl.slice(0, -1);
} }
if (!this.receptionTrUrl.endsWith('/typedrequest')) { if (!receptionBaseUrlArg.endsWith('/typedrequest')) {
this.receptionTrUrl = `${this.receptionTrUrl}/typedrequest`; receptionBaseUrlArg = `${receptionBaseUrlArg}/typedrequest`;
} }
console.log(`reception client connecting to ${this.receptionTrUrl}`); this.parsedReceptionUrl = plugins.smarturl.Smarturl.createFromUrl(receptionBaseUrlArg);
console.log(`reception client connecting to ${this.parsedReceptionUrl.toString()}`);
if (!appDataArg) { if (!appDataArg) {
appDataArg = { appDataArg = {
id: '', // TODO id: '', // TODO
@@ -39,6 +39,11 @@ export class IdpClient {
public requests = new IdpRequests(this); 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 * 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 * this message retrieves the app data when on the sso domain
@@ -123,7 +128,7 @@ export class IdpClient {
} }
const refreshJwtReq = const refreshJwtReq =
new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_RefreshJwt>( new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_RefreshJwt>(
`${this.receptionTrUrl}/typedrequest`, this.parsedReceptionUrl.toString(),
'refreshJwt' 'refreshJwt'
); );
const response = await refreshJwtReq.fire({ const response = await refreshJwtReq.fire({
@@ -146,7 +151,7 @@ export class IdpClient {
const extractedJwt = await this.helpers.extractDataFromJwtString(jwt); const extractedJwt = await this.helpers.extractDataFromJwtString(jwt);
const getTransferToken = const getTransferToken =
new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_ExchangeRefreshTokenAndTransferToken>( new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_ExchangeRefreshTokenAndTransferToken>(
`${this.receptionTrUrl}/typedrequest`, this.parsedReceptionUrl.toString(),
'exchangeRefreshTokenAndTransferToken' 'exchangeRefreshTokenAndTransferToken'
); );
const response = await getTransferToken.fire({ const response = await getTransferToken.fire({
@@ -184,7 +189,7 @@ export class IdpClient {
if (transferToken) { if (transferToken) {
const getTransferToken = const getTransferToken =
new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_ExchangeRefreshTokenAndTransferToken>( new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_ExchangeRefreshTokenAndTransferToken>(
`${this.receptionTrUrl}/typedrequest`, this.parsedReceptionUrl.toString(),
'exchangeRefreshTokenAndTransferToken' 'exchangeRefreshTokenAndTransferToken'
); );
const response = await getTransferToken.fire({ const response = await getTransferToken.fire({
@@ -231,15 +236,14 @@ export class IdpClient {
} else { } else {
if (requireLoginArg) { if (requireLoginArg) {
const urlInstance = plugins.smarturl.Smarturl.createFromUrl( const urlInstance = plugins.smarturl.Smarturl.createFromUrl(
'https://sso.workspace.global/', this.parsedReceptionUrl.clone().set('path', '/login').toString(),
{ {
searchParams: { searchParams: {
appdata: plugins.smartjson.stringifyBase64(this.appData), appdata: plugins.smartjson.stringifyBase64(this.appData),
action: 'login',
}, },
} }
); );
if (!globalThis.location.href.startsWith('https://sso.workspace.global/')) { if (!globalThis.location.href.startsWith(this.parsedReceptionUrl.toString())) {
globalThis.location.href = urlInstance.toString(); globalThis.location.href = urlInstance.toString();
} }
} }
@@ -265,7 +269,7 @@ export class IdpClient {
} else { } else {
// we are in the sso page // we are in the sso page
await this.enableTypedSocket(); await this.enableTypedSocket();
console.log(`logging out against ${this.receptionTrUrl}`) console.log(`logging out against ${this.parsedReceptionUrl.toString()}`);
const logoutTr = const logoutTr =
this.typedsocket.createTypedRequest<plugins.lointReception.request.ILogoutRequest>( this.typedsocket.createTypedRequest<plugins.lointReception.request.ILogoutRequest>(
'logout' 'logout'
@@ -292,7 +296,7 @@ export class IdpClient {
this.typedsocketDeferred.claim(); this.typedsocketDeferred.claim();
this.typedsocket = await plugins.typedsocket.TypedSocket.createClient( this.typedsocket = await plugins.typedsocket.TypedSocket.createClient(
this.typedrouter, this.typedrouter,
`${this.receptionTrUrl}/` this.parsedReceptionUrl.toString()
); );
this.typedsocketDeferred.resolve(this.typedsocket); this.typedsocketDeferred.resolve(this.typedsocket);
return this.typedsocketDeferred.promise; return this.typedsocketDeferred.promise;
+7 -7
View File
@@ -12,21 +12,21 @@ export class IdpRequests {
public get afterRegistrationEmailClicked () { public get afterRegistrationEmailClicked () {
return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_AfterRegistrationEmailClicked>( return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_AfterRegistrationEmailClicked>(
this.idpClientArg.receptionTrUrl, this.idpClientArg.parsedReceptionUrl.toString(),
'afterRegistrationEmailClicked' 'afterRegistrationEmailClicked'
); );
} }
public get setData() { public get setData() {
return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_SetDataForRegistration>( return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_SetDataForRegistration>(
this.idpClientArg.receptionTrUrl, this.idpClientArg.parsedReceptionUrl.toString(),
'setDataForRegistration' 'setDataForRegistration'
); );
} }
public get mobileNumberVerification () { public get mobileNumberVerification () {
return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_MobileVerificationForRegistration>( return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_MobileVerificationForRegistration>(
this.idpClientArg.receptionTrUrl, this.idpClientArg.parsedReceptionUrl.toString(),
'mobileVerificationForRegistration' 'mobileVerificationForRegistration'
); );
} }
@@ -34,28 +34,28 @@ export class IdpRequests {
public get finishRegistration() { public get finishRegistration() {
return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_FinishRegistration>( return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_FinishRegistration>(
this.idpClientArg.receptionTrUrl, this.idpClientArg.parsedReceptionUrl.toString(),
'finishRegistration' 'finishRegistration'
); );
} }
public get loginWithUserNameAndPassword () { public get loginWithUserNameAndPassword () {
return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_LoginWithEmailOrUsernameAndPassword>( return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_LoginWithEmailOrUsernameAndPassword>(
this.idpClientArg.receptionTrUrl, this.idpClientArg.parsedReceptionUrl.toString(),
'loginWithEmailOrUsernameAndPassword' 'loginWithEmailOrUsernameAndPassword'
); );
} }
public get obtainJwt () { public get obtainJwt () {
return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_RefreshJwt>( return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_RefreshJwt>(
this.idpClientArg.receptionTrUrl, this.idpClientArg.parsedReceptionUrl.toString(),
'refreshJwt' 'refreshJwt'
); );
} }
public get obtainOneTimeToken () { public get obtainOneTimeToken () {
return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_ExchangeRefreshTokenAndTransferToken>( return new plugins.typedrequest.TypedRequest<plugins.lointReception.request.IReq_ExchangeRefreshTokenAndTransferToken>(
this.idpClientArg.receptionTrUrl, this.idpClientArg.parsedReceptionUrl.toString(),
'exchangeRefreshTokenAndTransferToken' 'exchangeRefreshTokenAndTransferToken'
); );
} }
+1 -1
View File
@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@idp.global/idp.global', name: '@idp.global/idp.global',
version: '1.1.0', version: '1.2.2',
description: 'An identity provider software managing user authentications, registrations, and sessions.' description: 'An identity provider software managing user authentications, registrations, and sessions.'
} }
+146
View File
@@ -0,0 +1,146 @@
import * as plugins from '../plugins.js';
import {
customElement,
DeesElement,
property,
html,
type TemplateResult,
css,
cssManager,
query,
} from '@design.estate/dees-element';
import { commitinfo } from '../../dist_ts/00_commitinfo_data.js';
import { IdpState } from '../idp.state.js';
declare global {
interface HTMLElementTagNameMap {
'idp-centercontainer': IdpCenterContainer;
}
}
@customElement('idp-centercontainer')
export class IdpCenterContainer extends DeesElement {
public static demo = () => html`<idp-centercontainer></idp-centercontainer>`;
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
font-family: 'Geist Sans';
position: absolute;
width: 100%;
height: 100%;
}
.mainContainer {
position: absolute;
top: 0px;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
transition: all 0.1s;
transition-delay: 0.05s;
transform: translate3d(0px, 8px, 0px);
pointer-events: none;
}
.mainContainer.show {
opacity: 1;
pointer-events: all;
transform: translate3d(0px, 0px, 0px);
}
.loginblock {
max-width: 500px;
flex-grow: 1;
transform: translate3d(0px, 0px, 0px);
transition: all 0.2s;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
background: ${cssManager.bdTheme('#ffffff', '#111111')};
border-top: 1px solid ${cssManager.bdTheme('#ffffff', '#222222')};
border-radius: 16px;
overflow: hidden;
}
h1 {
font-size: 24px;
font-family: 'Cal Sans';
text-align: center;
letter-spacing:0.0125em;
}
.contentSpacer {
padding: 0px 0px 16px 0px;
}
.legalinfo {
text-align: center;
margin: auto;
color: ${cssManager.bdTheme('#666', '#ccc')};
font-size: 12px;
line-height: 100%;
padding: 8px;
background: ${cssManager.bdTheme('#f5f5f5', '#111')};
border-top: 1px solid ${cssManager.bdTheme('#ccc', '#222222')};
color: ${cssManager.bdTheme('#666', '#888')};
}
.legalinfo a {
color: ${cssManager.bdTheme('#666', '#ccc')};
text-decoration: none;
}
`,
];
render() {
return html`
<div class="mainContainer">
<div class="loginblock">
<h1>idp.global</h1>
<div class="contentSpacer">
<slot></slot>
</div>
<div class="legalinfo">
<a href="https://legal.task.vc/" target="_blank">Legal Info</a>
| <a href="https://task.vc/" target="_blank">Company Website</a>
| <a href="https://support.task.vc/" target="_blank">Support</a>
| idp.global v${commitinfo.version}
</div>
</div>
</div>
`;
}
public async show() {
await this.updateComplete;
const domtoolsInstance = await this.domtoolsPromise;
const done = plugins.smartpromise.defer();
requestAnimationFrame(async () => {
this.shadowRoot.querySelector('.mainContainer').classList.add('show');
await domtoolsInstance.convenience.smartdelay.delayFor(200);
done.resolve();
});
return done.promise;
}
public async hide() {
await this.updateComplete;
const domtoolsInstance = await this.domtoolsPromise;
const done = plugins.smartpromise.defer();
requestAnimationFrame(async () => {
this.shadowRoot.querySelector('.mainContainer').classList.remove('show');
await domtoolsInstance.convenience.smartdelay.delayFor(200);
done.resolve();
});
return done.promise;
}
}
-264
View File
@@ -1,264 +0,0 @@
import * as plugins from '../plugins.js';
import {
customElement,
DeesElement,
property,
html,
type TemplateResult,
css,
cssManager,
query,
} from '@design.estate/dees-element';
import { commitinfo } from '../../dist_ts/00_commitinfo_data.js';
declare global {
interface HTMLElementTagNameMap {
'idp-logincontainer': IdpLogincontainer;
}
}
@customElement('idp-logincontainer')
export class IdpLogincontainer extends DeesElement {
public static demo = () => html`<idp-logincontainer></idp-logincontainer>`;
@query('.loginPromptContainer')
loginPromptContainer: HTMLDivElement;
@query('.loginManagerContainer')
loginManagerContainer: HTMLDivElement
@query('.transferManagerContainer')
transferManagerContainer: HTMLDivElement
public receptionClient = new plugins.idpClient.IdpClient('https://reception.lossless.one:443', {
appUrl: 'https://sso.workspace.global/',
description: 'the central sso app for workspace.global',
logoUrl: 'https://assetbroker.lossless.one/some',
name: 'sso.workspace.global',
id: null,
});
constructor() {
super();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
font-family: 'Geist Sans';
position: absolute;
width: 100%;
height: 100%;
}
.mainContainer {
position: absolute;
top: 0px;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
opacity: 0;
transition: all 0.2s;
transition-delay: 0.2s;
transform: translate3d(0px, 20px, 0px);
pointer-events: none;
}
.loginPromptContainer.show {
opacity: 1;
pointer-events: all;
transform: translate3d(0px, 0px, 0px);
}
.loginManagerContainer.show {
opacity: 1;
pointer-events: all;
transform: translate3d(0px, 0px, 0px);
}
.transferManagerContainer.show {
opacity: 1;
pointer-events: all;
transform: translate3d(0px, 0px, 0px);
}
.loginblock {
max-width: 520px;
flex-grow: 1;
transform: translate3d(0px, 0px, 0px);
transition: all 0.2s;
box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.2);
background: ${cssManager.bdTheme('#ffffff', '#181818')};
border-top: 1px solid ${cssManager.bdTheme('#ffffff', '#333333')};
border-radius: 16px;
overflow: hidden;
}
img {
width: 130px;
min-height: 34.9px;
display: block;
margin: auto;
margin-top: 16px;
margin-bottom: 25px;
filter: ${cssManager.bdTheme('invert(1)', '')};
}
.legalinfo {
text-align: center;
margin: auto;
margin-top: 10px;
color: ${cssManager.bdTheme('#666', '#ccc')};
font-size: 12px;
line-height: 100%;
padding: 8px;
background: ${cssManager.bdTheme('#f5f5f5', '#111')};
border-top: 1px solid ${cssManager.bdTheme('#ccc', '#222222')};
color: ${cssManager.bdTheme('#666', '#888')};
}
.legalinfo a {
color: ${cssManager.bdTheme('#666', '#ccc')};
text-decoration: none;
}
`,
];
render() {
return html`
<div class="mainContainer loginPromptContainer">
<div class="loginblock">
<img
src="https://assetbroker.lossless.one/brandfiles/00general/plain_workspaceglobal.svg"
/>
<idp-login></idp-login>
<div class="legalinfo">
<a href="https://legal.task.vc/" target="_blank">Legal Info</a>
| <a href="https://task.vc/" target="_blank">Company Website</a>
| <a href="https://support.task.vc/" target="_blank">Support</a>
| SSO v${commitinfo.version}
</div>
</div>
</div>
<div class="mainContainer loginManagerContainer">
<div class="loginblock">
<img
src="https://assetbroker.lossless.one/brandfiles/00general/plain_workspaceglobal.svg"
/>
<div class="legalinfo">
<a href="https://legal.task.vc/" target="_blank">Legal Info</a>
| <a href="https://task.vc/" target="_blank">Company Website</a>
| <a href="https://support.task.vc/" target="_blank">Support</a>
| SSO v${commitinfo.version}
</div>
</div>
</div>
<div class="mainContainer transferManagerContainer">
<div class="loginblock">
<img
src="https://assetbroker.lossless.one/brandfiles/00general/plain_workspaceglobal.svg"
/>
<idp-transfermanager></idp-transfermanager>
<div class="legalinfo">
<a href="https://legal.task.vc/" target="_blank">Legal Info</a>
| <a href="https://task.vc/" target="_blank">Company Website</a>
| <a href="https://support.task.vc/" target="_blank">Support</a>
| SSO v${commitinfo.version}
</div>
</div>
</div>
`;
}
public async showComponent(componentNameArg: 'loginPrompt' | 'loginManager' | 'transferManager') {
const domtoolsInstance = await this.domtoolsPromise;
const containerItems: HTMLDivElement[] = [
this.loginPromptContainer,
this.loginManagerContainer,
this.transferManagerContainer,
];
const show = async (itemArg: HTMLDivElement) => {
for (const containerItem of containerItems) {
if (containerItem !== itemArg) {
containerItem.classList.remove('show');
}
}
await domtoolsInstance.convenience.smartdelay.delayFor(200);
itemArg.classList.add('show');
await domtoolsInstance.convenience.smartdelay.delayFor(200);
};
switch (componentNameArg) {
case 'loginPrompt':
await show(this.loginPromptContainer);
break;
case 'loginManager':
await show(this.loginManagerContainer);
break;
case 'transferManager':
await show(this.transferManagerContainer);
break;
}
}
public async determineNextAction() {
const domtoolsInstance = await this.domtoolsPromise;
let action: plugins.idpInterfaces.data.TLoginAction;
if (domtoolsInstance.router.queryParams.getQueryParam('action')) {
action = domtoolsInstance.router.queryParams.getQueryParam('action');
}
if (window.location.pathname === '/afterregistration') {
await this.domtools.convenience.smartdelay.delayFor(1000);
await this.receptionClient.determineLoginStatus();
await this.receptionClient.getTransferTokenAndSwitchToLocation('https://account.workspace.global')
} else if (!(await this.receptionClient.determineLoginStatus()) && action === 'login') {
this.showComponent('loginPrompt');
} else if ((await this.receptionClient.determineLoginStatus()) && action === 'login') {
await this.showComponent('transferManager');
const wgTransferManager = this.shadowRoot.querySelector('idp-transfermanager');
await wgTransferManager.handleTransfer();
} else if ((await this.receptionClient.determineLoginStatus()) && action === 'manage') {
this.showComponent('loginManager');
} else if (action === 'logout') {
console.log('logging out, since requested action is "logout"');
await this.receptionClient.logout();
} else {
this.showComponent('loginPrompt');
}
}
public async firstUpdated() {
const domtoolsInstance = await this.domtoolsPromise;
await domtoolsInstance.convenience.smartdelay.delayFor(0);
console.log(`your are loggedin: ${await await this.receptionClient.determineLoginStatus()}`);
let appData: plugins.idpInterfaces.data.IApp;
if (domtoolsInstance.router.queryParams.getQueryParam('appdata')) {
appData = domtoolsInstance.convenience.smartjson.parseBase64(
domtoolsInstance.router.queryParams.getQueryParam('appdata')
);
}
const idpLogin = this.shadowRoot.querySelector('idp-login');
const idpTransferManager = this.shadowRoot.querySelector('idp-transfermanager');
idpLogin.appData = appData;
idpTransferManager.appData = appData;
await this.determineNextAction();
idpLogin.jwtObserable.subscribe({
next: async (jwtArg) => {
console.log('loggedIn');
await this.receptionClient.storeJwt(jwtArg);
await this.determineNextAction();
},
});
idpLogin.dispatchJwt();
}
}
+34 -103
View File
@@ -17,20 +17,17 @@ import '@uptime.link/webwidget';
import '@design.estate/dees-catalog'; import '@design.estate/dees-catalog';
import { DeesForm, DeesFormSubmit, DeesInputText } from '@design.estate/dees-catalog'; import { DeesForm, DeesFormSubmit, DeesInputText } from '@design.estate/dees-catalog';
import { IdpState } from '../idp.state.js';
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
'idp-login': IdpLogin; 'idp-loginprompt': IdpLoginPrompt;
} }
} }
@customElement('idp-login') @customElement('idp-loginprompt')
export class IdpLogin extends DeesElement { export class IdpLoginPrompt extends DeesElement {
public static demo = () => html`<idp-login></idp-login>`; public static demo = () => html`<idp-loginprompt></idp-loginprompt>`;
public static receptionUrl = 'https://reception.lossless.one/typedrequest';
@property()
public activePane: 'login' | 'register' = 'login';
@property() @property()
public productOfInterest: string; public productOfInterest: string;
@@ -60,56 +57,19 @@ export class IdpLogin extends DeesElement {
color: ${cssManager.bdTheme('#333333', '#ffffff')}; color: ${cssManager.bdTheme('#333333', '#ffffff')};
} }
.box {
opacity: 0;
cursor: pointer;
overflow: hidden;
transition: all 0.2s ease;
height: 0px;
}
.box.active {
opacity: 1 !important;
height: 360px;
cursor: auto;
}
.loginbox {
}
.registerbox {
}
.boxcontent { .boxcontent {
margin: 0px 20px; margin: 0px 20px;
} }
.info {
text-align: center;
padding: 32px;
line-height: 1.5em;
font-size: 12px;
font-weight: 600;
color: #999;
}
.registerButton { .registerButton {
display: block; margin-top: 16px;
transition: all 0.2s ease;
will-change: transform;
cursor: pointer;
}
.registerButton:hover {
color: #fff;
transform: scale(1.02);
} }
`, `,
]; ];
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="loginbox box ${this.activePane === 'login' ? 'active' : ''}"> <idp-centercontainer>
<div class="boxcontent"> <div class="boxcontent">
<dees-form <dees-form
id="loginForm" id="loginForm"
@@ -133,37 +93,13 @@ export class IdpLogin extends DeesElement {
.isPasswordBool=${true} .isPasswordBool=${true}
></dees-input-text> ></dees-input-text>
<dees-form-submit id="loginSubmitButton"></dees-form-submit> <dees-form-submit id="loginSubmitButton"></dees-form-submit>
<div class="info">
You'll go here: ${this.appData ? html`${this.appData.appUrl}` : html``}
<p><span class="registerButton" @click=${() => {this.activePane = 'register'}}>You can also register for a new account.</span></p>
</div>
</dees-form> </dees-form>
<dees-button type="discreet" class="registerButton" @click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/register');
}}>Register instead</dees-button>
</div> </div>
</div> </idp-centercontainer>
<div class="registerbox box ${this.activePane === 'register' ? 'active' : ''}">
<div class="boxcontent">
<dees-form
id="registrationForm"
@formData="${(eventArg) => {
this.register({
emailAddress: eventArg.detail.data.emailAddress,
});
}}"
>
<dees-input-text
.required=${true}
key="emailAddress"
label="Email-Address"
></dees-input-text>
<dees-input-checkbox .label="${'Agree to the Terms and Conditions'}"></dees-input-checkbox>
<dees-form-submit>Send Verification Email</dees-form-submit>
<div class="info">
Already have an account?
<p><span class="registerButton" @click=${() => {this.activePane = 'login'}}>Login instead.</span></p>
</div>
</dees-form>
</div>
</div>
`; `;
} }
@@ -174,7 +110,7 @@ export class IdpLogin extends DeesElement {
const loginSubmitButton: DeesFormSubmit = loginForm.querySelector('#loginSubmitButton'); const loginSubmitButton: DeesFormSubmit = loginForm.querySelector('#loginSubmitButton');
const setButtonText = async () => { const setButtonText = async () => {
if (loginPasswordInput.value) { if (loginPasswordInput.value) {
console.log('updating text of loginprompt.') console.log('updating text of loginprompt.');
loginSubmitButton.text = 'Login'; loginSubmitButton.text = 'Login';
} else { } else {
loginSubmitButton.text = 'Send magic link (or enter password)'; loginSubmitButton.text = 'Send magic link (or enter password)';
@@ -192,12 +128,12 @@ export class IdpLogin extends DeesElement {
const loginForm: DeesForm = this.shadowRoot.querySelector('#loginForm'); const loginForm: DeesForm = this.shadowRoot.querySelector('#loginForm');
const loginRequestWithUsernameAndPassword = const loginRequestWithUsernameAndPassword =
new domtools.TypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmailOrUsernameAndPassword>( new domtools.TypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmailOrUsernameAndPassword>(
IdpLogin.receptionUrl, '/typedrequest',
'loginWithEmailOrUsernameAndPassword' 'loginWithEmailOrUsernameAndPassword'
); );
const loginRequestWithEmail = const loginRequestWithEmail =
new domtools.TypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmail>( new domtools.TypedRequest<plugins.idpInterfaces.request.IReq_LoginWithEmail>(
IdpLogin.receptionUrl, '/typedrequest',
'loginWithEmail' 'loginWithEmail'
); );
@@ -238,29 +174,6 @@ export class IdpLogin extends DeesElement {
} }
}; };
private register = async (valueArg: { emailAddress: string }) => {
const registrationForm: DeesForm = this.shadowRoot.querySelector('#registrationForm');
registrationForm.setStatus('pending', 'registering...');
const firstSignupRequest =
new domtools.TypedRequest<plugins.idpInterfaces.request.IReq_FirstRegistration>(
IdpLogin.receptionUrl,
'firstRegistrationRequest'
);
const response = await firstSignupRequest
.fire({
email: valueArg.emailAddress,
productSlugOfInterest: this.productOfInterest,
})
.catch((err) => {
registrationForm.setStatus('error', err.message);
return null;
});
if (response.status === 'ok') {
registrationForm.setStatus('success', 'Please check your email!');
}
console.log(response);
};
public async dispatchJwt(jwtArg?: string) { public async dispatchJwt(jwtArg?: string) {
if (jwtArg !== undefined) { if (jwtArg !== undefined) {
console.log(`dispatching jwt from loginprompt.`); console.log(`dispatching jwt from loginprompt.`);
@@ -281,7 +194,7 @@ export class IdpLogin extends DeesElement {
// a refreshToken binds dierctly to a session. // a refreshToken binds dierctly to a session.
// the refresh token is used on a continuous basis to get fresh and short-lived jwts // the refresh token is used on a continuous basis to get fresh and short-lived jwts
const refreshJwt = new domtools.TypedRequest<plugins.idpInterfaces.request.IReq_RefreshJwt>( const refreshJwt = new domtools.TypedRequest<plugins.idpInterfaces.request.IReq_RefreshJwt>(
IdpLogin.receptionUrl, '/typedrequest',
'refreshJwt' 'refreshJwt'
); );
const responseJwt = await refreshJwt.fire({ const responseJwt = await refreshJwt.fire({
@@ -297,4 +210,22 @@ export class IdpLogin extends DeesElement {
return null; return null;
} }
} }
public async focus() {
(
this.shadowRoot.querySelector('#loginEmailInput') as plugins.deesCatalog.DeesInputText
).focus();
}
public async show() {
await this.updateComplete;
const centerContainer = this.shadowRoot.querySelector('idp-centercontainer');
await centerContainer.show();
}
public async hide() {
await this.updateComplete;
const centerContainer = this.shadowRoot.querySelector('idp-centercontainer');
await centerContainer.hide();
}
} }
+204
View File
@@ -0,0 +1,204 @@
import * as plugins from '../plugins.js';
import {
customElement,
DeesElement,
property,
html,
type TemplateResult,
css,
cssManager,
state,
domtools,
} from '@design.estate/dees-element';
// third party catalogs
import '@uptime.link/webwidget';
import '@design.estate/dees-catalog';
import { DeesForm, DeesFormSubmit, DeesInputText } from '@design.estate/dees-catalog';
import { IdpState } from '../idp.state.js';
declare global {
interface HTMLElementTagNameMap {
'idp-registrationprompt': IdpRegistrationPrompt;
}
}
@customElement('idp-registrationprompt')
export class IdpRegistrationPrompt extends DeesElement {
public static demo = () => html`<idp-login></idp-login>`;
@property()
public productOfInterest: string;
@property()
jwt: string;
@property({
reflect: true,
type: Object,
})
appData: plugins.idpInterfaces.data.IApp;
public jwtObserable = new domtools.plugins.smartrx.rxjs.Subject<string>();
constructor() {
super();
domtools.elementBasic.setup();
}
public static styles = [
cssManager.defaultStyles,
css`
:host {
font-family: 'Geist Sans';
display: block;
color: ${cssManager.bdTheme('#333333', '#ffffff')};
}
.boxcontent {
margin: 0px 20px;
}
.registerButton {
display: block;
transition: all 0.2s ease;
will-change: transform;
cursor: pointer;
}
.registerButton:hover {
color: #fff;
transform: scale(1.02);
}
.loginButton {
margin-top: 16px;
}
`,
];
public render(): TemplateResult {
return html`
<idp-centercontainer>
<div class="boxcontent">
<dees-form
id="registrationForm"
@formData="${(eventArg) => {
this.register({
emailAddress: eventArg.detail.data.emailAddress,
});
}}"
>
<dees-input-text
.required=${true}
key="emailAddress"
label="Email-Address"
></dees-input-text>
<dees-input-checkbox
.label="${'Agree to the Terms and Conditions'}"
></dees-input-checkbox>
<dees-form-submit>Send Verification Email</dees-form-submit>
</dees-form>
<dees-button type="discreet" class="loginButton" @click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/login');
}}>Login instead</dees-button>
</div>
</idp-centercontainer>
`;
}
public async firstUpdated() {
const domtoolsInstance = await this.domtoolsPromise;
const loginForm: DeesForm = this.shadowRoot.querySelector('#loginForm');
const loginPasswordInput: DeesInputText = loginForm.querySelector('#loginPasswordInput');
const loginSubmitButton: DeesFormSubmit = loginForm.querySelector('#loginSubmitButton');
const setButtonText = async () => {
if (loginPasswordInput.value) {
console.log('updating text of registrationprompt.');
loginSubmitButton.text = 'Login';
} else {
loginSubmitButton.text = 'Send magic link (or enter password)';
}
};
loginForm.changeSubject.subscribe(() => {
console.log(`checking button text ${loginPasswordInput.value}`);
setButtonText();
});
setButtonText();
}
private register = async (valueArg: { emailAddress: string }) => {
const registrationForm: DeesForm = this.shadowRoot.querySelector('#registrationForm');
registrationForm.setStatus('pending', 'registering...');
const firstSignupRequest =
new domtools.TypedRequest<plugins.idpInterfaces.request.IReq_FirstRegistration>(
'/typedrequest',
'firstRegistrationRequest'
);
const response = await firstSignupRequest
.fire({
email: valueArg.emailAddress,
productSlugOfInterest: this.productOfInterest,
})
.catch((err) => {
registrationForm.setStatus('error', err.message);
return null;
});
if (response.status === 'ok') {
registrationForm.setStatus('success', 'Please check your email!');
}
console.log(response);
};
public async dispatchJwt(jwtArg?: string) {
if (jwtArg !== undefined) {
console.log(`dispatching jwt from loginprompt.`);
this.jwt = jwtArg;
await domtools.plugins.smartdelay.delayFor(200);
this.dispatchEvent(
new CustomEvent('leleLoginGotJwt', {
detail: {
jwt: this.jwt,
},
})
);
this.jwtObserable.next(this.jwt);
}
}
public async handleRefreshToken(refreshTokenArg: string, delayDispatchMillisArg = 0) {
// a refreshToken binds directly to a session.
// the refresh token is used on a continuous basis to get fresh and short-lived jwts
const refreshJwt = new domtools.TypedRequest<plugins.idpInterfaces.request.IReq_RefreshJwt>(
'/typedrequest',
'refreshJwt'
);
const responseJwt = await refreshJwt.fire({
refreshToken: refreshTokenArg,
});
if (responseJwt.jwt) {
this.domtools.convenience.smartdelay.delayFor(delayDispatchMillisArg).then(() => {
this.dispatchJwt(responseJwt.jwt);
});
return responseJwt.jwt;
} else {
return null;
}
}
public async show() {
await this.updateComplete;
const centerContainer = this.shadowRoot.querySelector('idp-centercontainer');
await centerContainer.show();
}
public async hide() {
await this.updateComplete;
const centerContainer = this.shadowRoot.querySelector('idp-centercontainer');
await centerContainer.hide();
}
}
+207 -216
View File
@@ -14,8 +14,6 @@ import {
@customElement('idp-registration-stepper') @customElement('idp-registration-stepper')
export class IdpRegistrationStepper extends DeesElement { export class IdpRegistrationStepper extends DeesElement {
public idpState = IdpState.getSingletonInstance();
@state() @state()
private usedSubTemplate: TemplateResult; private usedSubTemplate: TemplateResult;
@@ -66,231 +64,224 @@ export class IdpRegistrationStepper extends DeesElement {
} }
public async firstUpdated() { public async firstUpdated() {
const idpState = await IdpState.getSingletonInstance();
await this.domtoolsPromise; await this.domtoolsPromise;
this.domtools.router.on(`/finishregistration`, async (routeArg) => { const parsedUrl = plugins.smarturl.Smarturl.createFromUrl(window.location.href);
this.storedData.validationTokenUrlParam = routeArg.queryParams.validationtoken; this.storedData.validationTokenUrlParam = parsedUrl.searchParams['validationtoken'];
if (!this.storedData.validationTokenUrlParam) { console.log(`validationToken is ${this.storedData.validationTokenUrlParam}`);
this.usedSubTemplate = html` if (!this.storedData.validationTokenUrlParam) {
You need a validation token, but we couldn't find one. Please contact workspace.global support. this.usedSubTemplate = html`
`; You need a validation token, but we couldn't find one. Please contact workspace.global
await this.domtools.convenience.smartdelay.delayFor(5000); support.
this.usedSubTemplate = html` Redirecting you to workspace.global support... `; `;
await this.domtools.convenience.smartdelay.delayFor(2000); await this.domtools.convenience.smartdelay.delayFor(5000);
window.location.href = 'https://support.workspace.global'; window.location.href = '/';
return; return;
} }
// lets verify the info; // lets verify the info;
let tokenErrorMessage: string; let tokenErrorMessage: string;
const resAfterRegEmailClicked = const resAfterRegEmailClicked = await idpState.idpClient.requests.afterRegistrationEmailClicked
await this.idpState.idpClient.requests.afterRegistrationEmailClicked .fire({
.fire({ token: this.storedData.validationTokenUrlParam,
token: this.storedData.validationTokenUrlParam, })
}) .catch(
.catch( (
( err: (typeof DeesElement)['prototype']['domtools']['convenience']['typedrequest']['TypedResponseError']['prototype']
err: typeof DeesElement['prototype']['domtools']['convenience']['typedrequest']['TypedResponseError']['prototype'] ) => {
) => { tokenErrorMessage = err.errorText;
tokenErrorMessage = err.errorText; return;
return; }
} );
);
if (!resAfterRegEmailClicked || !resAfterRegEmailClicked.email) { console.log(resAfterRegEmailClicked);
this.usedSubTemplate = html`
the supplied validation token does not match any registration sessions.<br />
${tokenErrorMessage ? html`Reason: ${tokenErrorMessage}` : null}
`;
await this.domtools.convenience.smartdelay.delayFor(5000);
this.usedSubTemplate = html`redirecting you for further support... `;
await this.domtools.convenience.smartdelay.delayFor(1000);
window.location.href = 'https://support.workspace.global';
return;
} else {
this.storedData.email = resAfterRegEmailClicked.email;
}
// lets continue with UI if (!resAfterRegEmailClicked || !resAfterRegEmailClicked.email) {
this.usedSubTemplate = html`<dees-stepper this.usedSubTemplate = html`
.steps=${[ the supplied validation token does not match any registration sessions.<br />
{ ${tokenErrorMessage ? html`Reason: ${tokenErrorMessage}` : null}
title: 'What is your name?', `;
content: html` await this.domtools.convenience.smartdelay.delayFor(5000);
<dees-form> idpState.domtools.router.pushUrl('/');
<dees-input-text return;
key="email" } else {
label="Your Email" this.storedData.email = resAfterRegEmailClicked.email;
value="${this.storedData.email}" }
disabled
></dees-input-text>
<dees-input-text key="firstName" required label="First Name"></dees-input-text>
<dees-input-text key="lastName" required label="Last Name"></dees-input-text>
<dees-form-submit>Next</dees-form-submit>
</dees-form>
`,
validationFunc: async (stepperArg, elementArg) => {
const deesForm: plugins.deesCatalog.DeesForm = elementArg.querySelector('dees-form');
deesForm.addEventListener('formData', async (eventArg: CustomEvent) => {
const response = await this.idpState.idpClient.requests.setData
.fire({
token: this.storedData.validationTokenUrlParam,
userData: {
name: `${eventArg.detail.data.firstName} ${eventArg.detail.data.lastName}`,
connectedOrgs: null,
email: null,
status: null,
username: null,
},
})
.catch(
(
errArg: typeof DeesElement['prototype']['domtools']['convenience']['typedrequest']['TypedResponseError']['prototype']
) => {
deesForm.setStatus('error', errArg.errorText);
}
);
deesForm.setStatus('success', 'ok!');
stepperArg.goNext();
});
},
onReturnToStepFunc: async (stepperArg, stepElementArg) => {
const deesForm = stepElementArg.querySelector('dees-form');
deesForm.setStatus('normal', 'Edit and Next');
},
},
{
title: 'What is your mobile number?',
content: html`
<dees-form>
<dees-input-text
key="mobileNumber"
required
label="Your Mobile Number"
></dees-input-text>
<dees-form-submit>Next</dees-form-submit>
</dees-form>
`,
validationFunc: async (stepperArg, elementArg) => {
const deesForm: plugins.deesCatalog.DeesForm = elementArg.querySelector('dees-form');
deesForm.addEventListener('formData', async (eventArg: CustomEvent) => {
const response = await this.idpState.idpClient.requests.mobileNumberVerification
.fire({
token: this.storedData.validationTokenUrlParam,
mobileNumber: eventArg.detail.data.mobileNumber,
})
.catch(
(
errArg: typeof DeesElement['prototype']['domtools']['convenience']['typedrequest']['TypedResponseError']['prototype']
) => {
deesForm.setStatus('error', errArg.errorText);
}
);
deesForm.setStatus('success', 'ok!');
stepperArg.goNext();
});
},
onReturnToStepFunc: async (stepperArg, stepElementArg) => {
const deesForm = stepElementArg.querySelector('dees-form');
deesForm.setStatus('normal', 'Edit and Next');
},
},
{
title: 'What is the Verification Code?',
content: html`
<dees-form>
<dees-input-text
key="verificationCode"
required
label="Verification Code"
></dees-input-text>
<dees-form-submit>Next</dees-form-submit>
</dees-form>
`,
validationFunc: async (stepperArg, elementArg) => {
const deesForm: plugins.deesCatalog.DeesForm = elementArg.querySelector('dees-form');
deesForm.addEventListener('formData', async (eventArg: CustomEvent) => {
const response = await this.idpState.idpClient.requests.mobileNumberVerification.fire({
token: this.storedData.validationTokenUrlParam,
verificationCode: eventArg.detail.data.verificationCode,
});
if (response.verficationCodeOk) { // lets continue with UI
deesForm.setStatus('success', 'ok!'); this.usedSubTemplate = html`<dees-stepper
stepperArg.goNext(); .steps=${[
} else { {
deesForm.setStatus('error', 'wrong code!'); title: 'What is your name?',
await this.domtools.convenience.smartdelay.delayFor(3000); content: html`
deesForm.setStatus('normal', 'Retry And Next!'); <dees-form>
} <dees-input-text
}); key="email"
}, label="Your Email"
onReturnToStepFunc: async (stepperArg, stepElementArg) => { value="${this.storedData.email}"
stepperArg.goBack(); disabled
const deesForm = stepElementArg.querySelector('dees-form'); ></dees-input-text>
deesForm.setStatus('normal', 'Next'); <dees-input-text key="firstName" required label="First Name"></dees-input-text>
}, <dees-input-text key="lastName" required label="Last Name"></dees-input-text>
}, <dees-form-submit>Next</dees-form-submit>
{ </dees-form>
title: 'Create a secure password:', `,
content: html` validationFunc: async (stepperArg, elementArg) => {
<dees-form> const deesForm: plugins.deesCatalog.DeesForm = elementArg.querySelector('dees-form');
<dees-input-text deesForm.addEventListener('formData', async (eventArg: CustomEvent) => {
key="password" const response = await idpState.idpClient.requests.setData
required .fire({
label="Your New Secure Password"
></dees-input-text>
<dees-form-submit>Next</dees-form-submit>
</dees-form>
`,
validationFunc: async (stepperArg, elementArg) => {
const deesForm: plugins.deesCatalog.DeesForm = elementArg.querySelector('dees-form');
deesForm.addEventListener('formData', async (eventArg: CustomEvent) => {
const response = await this.idpState.idpClient.requests.setData.fire({
token: this.storedData.validationTokenUrlParam, token: this.storedData.validationTokenUrlParam,
userData: { userData: {
username: null, name: `${eventArg.detail.data.firstName} ${eventArg.detail.data.lastName}`,
email: null,
name: null,
connectedOrgs: null, connectedOrgs: null,
email: null,
status: null, status: null,
password: eventArg.detail.data.password, username: null,
}, },
}); })
const finishRegistrationResponse = .catch(
await this.idpState.idpClient.requests.finishRegistration.fire({ (
token: this.storedData.validationTokenUrlParam, errArg: (typeof DeesElement)['prototype']['domtools']['convenience']['typedrequest']['TypedResponseError']['prototype']
}); ) => {
deesForm.setStatus('pending', 'User created!'); deesForm.setStatus('error', errArg.errorText);
await this.domtools.convenience.smartdelay.delayFor(500);
deesForm.setStatus('pending', 'Obtaining Refresh Token...');
const loginResponse = await this.idpState.idpClient.requests.loginWithUserNameAndPassword.fire(
{
username: this.storedData.email,
password: eventArg.detail.data.password,
} }
); );
this.storedData.refreshToken = loginResponse.refreshToken; deesForm.setStatus('success', 'ok!');
stepperArg.goNext();
deesForm.setStatus('pending', 'Obtaining JWT...'); });
const jwtResponse = await this.idpState.idpClient.requests.obtainJwt.fire({
refreshToken: this.storedData.refreshToken,
});
deesForm.setStatus('pending', 'Obtaining Transfer Token...');
await this.idpState.idpClient.setJwt(jwtResponse.jwt);
await this.idpState.idpClient.getTransferTokenAndSwitchToLocation('https://sso.workspace.global/afterregistration');
});
},
}, },
] as plugins.deesCatalog.IStep[]} onReturnToStepFunc: async (stepperArg, stepElementArg) => {
></dees-stepper>`; const deesForm = stepElementArg.querySelector('dees-form');
await this.domtools.convenience.smartdelay.delayFor(100); deesForm.setStatus('normal', 'Edit and Next');
}); },
this.domtools.router.on('/', async () => { },
this.usedSubTemplate = html`Hm, this is app is not meant for what you are trying to do :) `; {
await this.domtools.convenience.smartdelay.delayFor(2000); title: 'What is your mobile number?',
this.usedSubTemplate = html`Redirecting you now...`; content: html`
window.location.href = `https://workspace.global`; <dees-form>
}); <dees-input-text
this.domtools.router._handleRouteState(); key="mobileNumber"
required
label="Your Mobile Number"
></dees-input-text>
<dees-form-submit>Next</dees-form-submit>
</dees-form>
`,
validationFunc: async (stepperArg, elementArg) => {
const deesForm: plugins.deesCatalog.DeesForm = elementArg.querySelector('dees-form');
deesForm.addEventListener('formData', async (eventArg: CustomEvent) => {
const response = await idpState.idpClient.requests.mobileNumberVerification
.fire({
token: this.storedData.validationTokenUrlParam,
mobileNumber: eventArg.detail.data.mobileNumber,
})
.catch(
(
errArg: (typeof DeesElement)['prototype']['domtools']['convenience']['typedrequest']['TypedResponseError']['prototype']
) => {
deesForm.setStatus('error', errArg.errorText);
}
);
deesForm.setStatus('success', 'ok!');
stepperArg.goNext();
});
},
onReturnToStepFunc: async (stepperArg, stepElementArg) => {
const deesForm = stepElementArg.querySelector('dees-form');
deesForm.setStatus('normal', 'Edit and Next');
},
},
{
title: 'What is the Verification Code?',
content: html`
<dees-form>
<dees-input-text
key="verificationCode"
required
label="Verification Code"
></dees-input-text>
<dees-form-submit>Next</dees-form-submit>
</dees-form>
`,
validationFunc: async (stepperArg, elementArg) => {
const deesForm: plugins.deesCatalog.DeesForm = elementArg.querySelector('dees-form');
deesForm.addEventListener('formData', async (eventArg: CustomEvent) => {
const response = await idpState.idpClient.requests.mobileNumberVerification.fire({
token: this.storedData.validationTokenUrlParam,
verificationCode: eventArg.detail.data.verificationCode,
});
if (response.verficationCodeOk) {
deesForm.setStatus('success', 'ok!');
stepperArg.goNext();
} else {
deesForm.setStatus('error', 'wrong code!');
await this.domtools.convenience.smartdelay.delayFor(3000);
deesForm.setStatus('normal', 'Retry And Next!');
}
});
},
onReturnToStepFunc: async (stepperArg, stepElementArg) => {
stepperArg.goBack();
const deesForm = stepElementArg.querySelector('dees-form');
deesForm.setStatus('normal', 'Next');
},
},
{
title: 'Create a secure password:',
content: html`
<dees-form>
<dees-input-text
key="password"
required
label="Your New Secure Password"
></dees-input-text>
<dees-form-submit>Next</dees-form-submit>
</dees-form>
`,
validationFunc: async (stepperArg, elementArg) => {
const deesForm: plugins.deesCatalog.DeesForm = elementArg.querySelector('dees-form');
deesForm.addEventListener('formData', async (eventArg: CustomEvent) => {
const response = await idpState.idpClient.requests.setData.fire({
token: this.storedData.validationTokenUrlParam,
userData: {
username: null,
email: null,
name: null,
connectedOrgs: null,
status: null,
password: eventArg.detail.data.password,
},
});
const finishRegistrationResponse =
await idpState.idpClient.requests.finishRegistration.fire({
token: this.storedData.validationTokenUrlParam,
});
deesForm.setStatus('pending', 'User created!');
await this.domtools.convenience.smartdelay.delayFor(500);
deesForm.setStatus('pending', 'Obtaining Refresh Token...');
const loginResponse =
await idpState.idpClient.requests.loginWithUserNameAndPassword.fire({
username: this.storedData.email,
password: eventArg.detail.data.password,
});
this.storedData.refreshToken = loginResponse.refreshToken;
deesForm.setStatus('pending', 'Obtaining JWT...');
const jwtResponse = await idpState.idpClient.requests.obtainJwt.fire({
refreshToken: this.storedData.refreshToken,
});
deesForm.setStatus('pending', 'Obtaining Transfer Token...');
await idpState.idpClient.setJwt(jwtResponse.jwt);
await idpState.idpClient.getTransferTokenAndSwitchToLocation(
'https://sso.workspace.global/afterregistration'
);
});
},
},
] as plugins.deesCatalog.IStep[]}
></dees-stepper>`;
await this.domtools.convenience.smartdelay.delayFor(100);
} }
} }
+14 -4
View File
@@ -12,6 +12,7 @@ import {
type TemplateResult, type TemplateResult,
} from '@design.estate/dees-element'; } from '@design.estate/dees-element';
import type { IdpViewcontainer } from '../views/viewcontainer.js'; import type { IdpViewcontainer } from '../views/viewcontainer.js';
import { IdpState } from '../idp.state.js';
@customElement('idp-welcome') @customElement('idp-welcome')
export class IdpWelcome extends DeesElement { export class IdpWelcome extends DeesElement {
@@ -36,6 +37,10 @@ export class IdpWelcome extends DeesElement {
h1 { h1 {
font-family: 'Cal Sans'; font-family: 'Cal Sans';
text-align: center; text-align: center;
font-size: 24px;
margin: 0px auto;
padding: 24px 24px 0px 24px;
width: 500px;
letter-spacing:0.0125em; letter-spacing:0.0125em;
} }
@@ -43,7 +48,8 @@ export class IdpWelcome extends DeesElement {
margin: 24px auto; margin: 24px auto;
width: 500px; width: 500px;
background: #111111; background: #111111;
border-radius: 8px; border-radius: 16px;
border-top: 1px solid ${cssManager.bdTheme('#ffffff', '#222222')};
padding: 24px; padding: 24px;
} }
@@ -60,10 +66,14 @@ export class IdpWelcome extends DeesElement {
<div class="textbox"> <div class="textbox">
Do you want to sign in or register? Do you want to sign in or register?
<dees-button @click=${() => { <dees-button @click=${async () => {
this.viewContainer.loadElement(elements.IdpLogincontainer); const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/login');
}}>Sign In</dees-button> }}>Sign In</dees-button>
<dees-button @click=${() => {}}>Register</dees-button> <dees-button @click=${async () => {
const idpState = await IdpState.getSingletonInstance();
idpState.domtools.router.pushUrl('/register');
}}>Register</dees-button>
</div> </div>
<div class="textbox"> <div class="textbox">
+2 -1
View File
@@ -1,5 +1,6 @@
export * from './idp-registration-stepper.js'; export * from './idp-registration-stepper.js';
export * from './idp-logincontainer.js'; export * from './idp-centercontainer.js';
export * from './idp-loginprompt.js'; export * from './idp-loginprompt.js';
export * from './idp-registerprompt.js';
export * from './idp-transfermanager.js'; export * from './idp-transfermanager.js';
export * from './idp-welcome.js'; export * from './idp-welcome.js';
+51 -7
View File
@@ -1,17 +1,61 @@
import * as plugins from './plugins.js'; import * as plugins from './plugins.js';
import { domtools } from '@design.estate/dees-element'
export class IdpState { export class IdpState {
// STATIC // STATIC
public static getSingletonInstance() { private static idpStateDeferred = plugins.smartpromise.defer<IdpState>();
if (!this.instance) { public static async getSingletonInstance() {
this.instance = new IdpState(); if (!this.idpStateDeferred.claimed) {
this.idpStateDeferred.claim();
const newIdpState = new IdpState();
await newIdpState.init();
this.idpStateDeferred.resolve(newIdpState);
} }
return this.instance; return this.idpStateDeferred.promise;
} }
private static instance: IdpState;
// INSTANCE // INSTANCE
public receptionUrl = 'https://reception.lossless.one/typedrequest'; public receptionUrl = window.location.origin;
public idpClient = new plugins.idpClient.IdpClient(this.receptionUrl); public idpClient = new plugins.idpClient.IdpClient(this.receptionUrl);
public domtools: domtools.DomTools;
public mainStatePart: plugins.deesDomtools.plugins.smartstate.StatePart<'main', {
view: 'welcome' | 'login' | 'register' | 'finishregistration';
}>
public async init() {
const domtoolsInstance = await domtools.DomTools.setupDomTools();
this.domtools = domtoolsInstance;
const state = new plugins.deesDomtools.plugins.smartstate.Smartstate<'main'>();
this.mainStatePart = await state.getStatePart('main', {
view: 'welcome',
}, 'soft');
this.domtools.router.on('/', async () => {
await this.mainStatePart.setState({
...this.mainStatePart.getState(),
view: 'welcome',
})
});
this.domtools.router.on('/login', async () => {
await this.mainStatePart.setState({
...this.mainStatePart.getState(),
view: 'login',
})
});
this.domtools.router.on('/register', async () => {
await this.mainStatePart.setState({
...this.mainStatePart.getState(),
view: 'register',
})
});
this.domtools.router.on('/finishregistration', async () => {
await this.mainStatePart.setState({
...this.mainStatePart.getState(),
view: 'finishregistration',
})
});
this.domtools.router._handleRouteState();
}
} }
+1 -3
View File
@@ -44,7 +44,7 @@ const run = async () => {
}, },
}); });
const serviceWorker = await serviceworker.getServiceworkerClient(); // const serviceWorker = await serviceworker.getServiceworkerClient();
const mainTemplate = html` const mainTemplate = html`
<style> <style>
@@ -58,8 +58,6 @@ const run = async () => {
render(mainTemplate, document.body); render(mainTemplate, document.body);
const viewContainer: IdpViewcontainer = document.querySelector('idp-viewcontainer');
viewContainer.loadElement(IdpWelcome);
}; };
+8 -1
View File
@@ -13,6 +13,13 @@ export { typedrequest };
// @design.estate scope // @design.estate scope
import * as deesCatalog from '@design.estate/dees-catalog'; import * as deesCatalog from '@design.estate/dees-catalog';
import * as deesDomtools from '@design.estate/dees-domtools';
import * as deesElement from '@design.estate/dees-element'; import * as deesElement from '@design.estate/dees-element';
export { deesCatalog, deesElement }; export { deesCatalog, deesDomtools, deesElement };
// @push.rocks scope
import * as smartpromise from '@push.rocks/smartpromise';
import * as smarturl from '@push.rocks/smarturl';
export { smartpromise, smarturl };
+38 -6
View File
@@ -1,4 +1,6 @@
import { IdpState } from '../idp.state.js';
import * as plugins from '../plugins.js'; import * as plugins from '../plugins.js';
import * as elements from '../elements/index.js';
import { import {
customElement, customElement,
@@ -32,21 +34,20 @@ export class IdpViewcontainer extends DeesElement {
min-width: 100vh; min-width: 100vh;
min-height: 100vh; min-height: 100vh;
} }
`,
`,
]; ];
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<style></style> <style></style>
<div class="viewContainer"> <div class="viewContainer"></div>
</div>
`; `;
} }
public currentElement: plugins.deesElement.DeesElement; public currentElement: plugins.deesElement.DeesElement;
public async loadElement(viewElement: typeof plugins.deesElement.DeesElement) { public async loadElement(viewElement: typeof plugins.deesElement.DeesElement) {
const idpState = await IdpState.getSingletonInstance();
// Wait until the viewContainer itself is rendered // Wait until the viewContainer itself is rendered
await this.updateComplete; await this.updateComplete;
@@ -60,18 +61,49 @@ export class IdpViewcontainer extends DeesElement {
// Remove the current element if it exists // Remove the current element if it exists
if (this.currentElement) { if (this.currentElement) {
const currentElement = this.currentElement as any;
if (currentElement.hide) {
await currentElement.hide();
}
viewContainer.removeChild(this.currentElement); viewContainer.removeChild(this.currentElement);
} }
// Create a new instance of the viewElement // Create a new instance of the viewElement
const newElement = new viewElement(); const newElement = new viewElement() as any;
(newElement as any).viewContainer = this; (newElement as any).viewContainer = this;
viewContainer.appendChild(newElement); viewContainer.appendChild(newElement);
if (newElement.show) {
await newElement.show();
}
// Wait until the new element is fully rendered // Wait until the new element is fully rendered
await newElement.updateComplete; await newElement.updateComplete;
// Set the new element as the current element // Set the new element as the current element
this.currentElement = newElement; this.currentElement = newElement;
} }
public async firstUpdated() {
const idpState = await IdpState.getSingletonInstance();
idpState.mainStatePart
.select((stateArg) => stateArg.view)
.subscribe(async (viewArg) => {
switch (viewArg) {
case 'welcome':
await this.loadElement(elements.IdpWelcome);
break;
case 'login':
console.log('now on /login');
await this.loadElement(elements.IdpLoginPrompt);
break;
case 'register':
await this.loadElement(elements.IdpRegistrationPrompt);
break;
case 'finishregistration':
await this.loadElement(elements.IdpRegistrationStepper);
}
});
}
} }