@descope/angular-sdk 0.0.0-next-a6b67f6a-20231228 → 0.0.0-next-cc7eda17-20231228

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/.editorconfig +16 -0
  2. package/.eslintrc.json +40 -0
  3. package/.github/workflows/ci.yml +98 -0
  4. package/.github/workflows/publish-next.yml +43 -0
  5. package/.github/workflows/publish.yml +31 -0
  6. package/.github/workflows/release.yml +41 -0
  7. package/.husky/pre-commit +4 -0
  8. package/.prettierrc +8 -0
  9. package/.vscode/extensions.json +4 -0
  10. package/.vscode/launch.json +20 -0
  11. package/.vscode/settings.json +3 -0
  12. package/.vscode/tasks.json +42 -0
  13. package/angular.json +154 -0
  14. package/jest.config.js +17 -0
  15. package/package.json +1 -4
  16. package/projects/angular-sdk/.eslintrc.json +32 -0
  17. package/projects/angular-sdk/ng-package.json +8 -0
  18. package/projects/angular-sdk/src/environment.ts +3 -0
  19. package/projects/angular-sdk/src/lib/components/descope/descope.component.spec.ts +104 -0
  20. package/projects/angular-sdk/src/lib/components/descope/descope.component.ts +114 -0
  21. package/projects/angular-sdk/src/lib/components/sign-in-flow/sign-in-flow.component.html +15 -0
  22. package/projects/angular-sdk/src/lib/components/sign-in-flow/sign-in-flow.component.spec.ts +53 -0
  23. package/projects/angular-sdk/src/lib/components/sign-in-flow/sign-in-flow.component.ts +32 -0
  24. package/projects/angular-sdk/src/lib/components/sign-up-flow/sign-up-flow.component.html +15 -0
  25. package/projects/angular-sdk/src/lib/components/sign-up-flow/sign-up-flow.component.spec.ts +51 -0
  26. package/projects/angular-sdk/src/lib/components/sign-up-flow/sign-up-flow.component.ts +32 -0
  27. package/projects/angular-sdk/src/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.html +15 -0
  28. package/projects/angular-sdk/src/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.spec.ts +53 -0
  29. package/projects/angular-sdk/src/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.ts +32 -0
  30. package/projects/angular-sdk/src/lib/descope-auth.module.ts +46 -0
  31. package/projects/angular-sdk/src/lib/services/descope-auth.guard.spec.ts +76 -0
  32. package/projects/angular-sdk/src/lib/services/descope-auth.guard.ts +16 -0
  33. package/projects/angular-sdk/src/lib/services/descope-auth.service.spec.ts +264 -0
  34. package/projects/angular-sdk/src/lib/services/descope-auth.service.ts +176 -0
  35. package/projects/angular-sdk/src/lib/services/descope.interceptor.spec.ts +102 -0
  36. package/projects/angular-sdk/src/lib/services/descope.interceptor.ts +76 -0
  37. package/projects/angular-sdk/src/lib/types/types.ts +10 -0
  38. package/projects/angular-sdk/src/lib/utils/constants.ts +8 -0
  39. package/projects/angular-sdk/src/lib/utils/helpers.spec.ts +103 -0
  40. package/projects/angular-sdk/src/lib/utils/helpers.ts +36 -0
  41. package/{dist/public-api.d.ts → projects/angular-sdk/src/public-api.ts} +4 -0
  42. package/projects/angular-sdk/tsconfig.lib.json +12 -0
  43. package/projects/angular-sdk/tsconfig.lib.prod.json +10 -0
  44. package/projects/angular-sdk/tsconfig.spec.json +11 -0
  45. package/projects/demo-app/.eslintrc.json +31 -0
  46. package/projects/demo-app/src/app/app-routing.module.ts +23 -0
  47. package/projects/demo-app/src/app/app.component.html +3 -0
  48. package/projects/demo-app/src/app/app.component.scss +16 -0
  49. package/projects/demo-app/src/app/app.component.spec.ts +37 -0
  50. package/projects/demo-app/src/app/app.component.ts +8 -0
  51. package/projects/demo-app/src/app/app.module.ts +52 -0
  52. package/projects/demo-app/src/app/home/home.component.html +23 -0
  53. package/projects/demo-app/src/app/home/home.component.scss +15 -0
  54. package/projects/demo-app/src/app/home/home.component.spec.ts +44 -0
  55. package/projects/demo-app/src/app/home/home.component.ts +61 -0
  56. package/projects/demo-app/src/app/interceptor/auth.interceptor.ts +20 -0
  57. package/projects/demo-app/src/app/login/login.component.html +12 -0
  58. package/projects/demo-app/src/app/login/login.component.spec.ts +42 -0
  59. package/projects/demo-app/src/app/login/login.component.ts +35 -0
  60. package/projects/demo-app/src/app/protected/protected.component.html +18 -0
  61. package/projects/demo-app/src/app/protected/protected.component.scss +8 -0
  62. package/projects/demo-app/src/app/protected/protected.component.spec.ts +42 -0
  63. package/projects/demo-app/src/app/protected/protected.component.ts +40 -0
  64. package/projects/demo-app/src/assets/.gitkeep +0 -0
  65. package/projects/demo-app/src/environments/conifg.ts +13 -0
  66. package/projects/demo-app/src/environments/environment.ts +19 -0
  67. package/projects/demo-app/src/favicon.ico +0 -0
  68. package/projects/demo-app/src/index.html +17 -0
  69. package/projects/demo-app/src/main.ts +7 -0
  70. package/projects/demo-app/src/styles.scss +21 -0
  71. package/projects/demo-app/tsconfig.app.json +10 -0
  72. package/projects/demo-app/tsconfig.spec.json +10 -0
  73. package/renovate.json +4 -0
  74. package/scripts/gitleaks/.gitleaks.toml +653 -0
  75. package/scripts/gitleaks/gitleaks.sh +34 -0
  76. package/scripts/setversion/setversion.js +20 -0
  77. package/setup-jest.ts +1 -0
  78. package/thirdPartyLicenseCollector_linux_amd64 +0 -0
  79. package/tsconfig.json +35 -0
  80. package/dist/environment.d.ts +0 -3
  81. package/dist/esm2022/descope-angular-sdk.mjs +0 -5
  82. package/dist/esm2022/environment.mjs +0 -4
  83. package/dist/esm2022/lib/components/descope/descope.component.mjs +0 -106
  84. package/dist/esm2022/lib/components/sign-in-flow/sign-in-flow.component.mjs +0 -40
  85. package/dist/esm2022/lib/components/sign-up-flow/sign-up-flow.component.mjs +0 -40
  86. package/dist/esm2022/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.mjs +0 -40
  87. package/dist/esm2022/lib/descope-auth.module.mjs +0 -52
  88. package/dist/esm2022/lib/services/descope-auth.guard.mjs +0 -15
  89. package/dist/esm2022/lib/services/descope-auth.service.mjs +0 -139
  90. package/dist/esm2022/lib/services/descope.interceptor.mjs +0 -51
  91. package/dist/esm2022/lib/types/types.mjs +0 -6
  92. package/dist/esm2022/lib/utils/constants.mjs +0 -7
  93. package/dist/esm2022/lib/utils/helpers.mjs +0 -27
  94. package/dist/esm2022/public-api.mjs +0 -13
  95. package/dist/fesm2022/descope-angular-sdk.mjs +0 -500
  96. package/dist/fesm2022/descope-angular-sdk.mjs.map +0 -1
  97. package/dist/index.d.ts +0 -5
  98. package/dist/lib/components/descope/descope.component.d.ts +0 -32
  99. package/dist/lib/components/sign-in-flow/sign-in-flow.component.d.ts +0 -24
  100. package/dist/lib/components/sign-up-flow/sign-up-flow.component.d.ts +0 -24
  101. package/dist/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.d.ts +0 -24
  102. package/dist/lib/descope-auth.module.d.ts +0 -14
  103. package/dist/lib/services/descope-auth.guard.d.ts +0 -2
  104. package/dist/lib/services/descope-auth.service.d.ts +0 -38
  105. package/dist/lib/services/descope.interceptor.d.ts +0 -2
  106. package/dist/lib/types/types.d.ts +0 -8
  107. package/dist/lib/utils/constants.d.ts +0 -5
  108. package/dist/lib/utils/helpers.d.ts +0 -5
  109. package/dist/package.json +0 -42
  110. /package/{dist → projects/angular-sdk}/README.md +0 -0
@@ -0,0 +1,104 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+ import { DescopeComponent } from './descope.component';
4
+ import createSdk from '@descope/web-js-sdk';
5
+ import { DescopeAuthConfig } from '../../types/types';
6
+ import { CUSTOM_ELEMENTS_SCHEMA, EventEmitter } from '@angular/core';
7
+ import mocked = jest.mocked;
8
+
9
+ jest.mock('@descope/web-js-sdk');
10
+ //Mock DescopeWebComponent
11
+ jest.mock('@descope/web-component', () => {
12
+ return jest.fn(() => {
13
+ // Create a mock DOM element
14
+ return document.createElement('descope-wc');
15
+ });
16
+ });
17
+
18
+ describe('DescopeComponent', () => {
19
+ let component: DescopeComponent;
20
+ let fixture: ComponentFixture<DescopeComponent>;
21
+ let mockedCreateSdk: jest.Mock;
22
+ const onSessionTokenChangeSpy = jest.fn();
23
+ const onUserChangeSpy = jest.fn();
24
+ const afterRequestHooksSpy = jest.fn();
25
+ const mockConfig: DescopeAuthConfig = {
26
+ projectId: 'someProject'
27
+ };
28
+
29
+ beforeEach(() => {
30
+ mockedCreateSdk = mocked(createSdk);
31
+
32
+ mockedCreateSdk.mockReturnValue({
33
+ onSessionTokenChange: onSessionTokenChangeSpy,
34
+ onUserChange: onUserChangeSpy,
35
+ httpClient: {
36
+ hooks: {
37
+ afterRequest: afterRequestHooksSpy
38
+ }
39
+ }
40
+ });
41
+
42
+ TestBed.configureTestingModule({
43
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
44
+ providers: [
45
+ DescopeAuthConfig,
46
+ { provide: DescopeAuthConfig, useValue: mockConfig }
47
+ ]
48
+ });
49
+
50
+ fixture = TestBed.createComponent(DescopeComponent);
51
+ component = fixture.componentInstance;
52
+ component.projectId = '123';
53
+ component.flowId = 'sign-in';
54
+ component.locale = 'en-US';
55
+ component.success = new EventEmitter<void>();
56
+ component.error = new EventEmitter<void>();
57
+ component.logger = { info: jest.fn(), error: jest.fn(), warn: jest.fn() };
58
+ component.errorTransformer = jest.fn();
59
+ fixture.detectChanges();
60
+ });
61
+
62
+ it('should create', () => {
63
+ expect(component).toBeTruthy();
64
+ const html: HTMLElement = fixture.nativeElement;
65
+ const webComponentHtml = html.querySelector('descope-wc');
66
+ expect(webComponentHtml).toBeDefined();
67
+ });
68
+
69
+ it('should correctly setup attributes based on inputs', () => {
70
+ const html: HTMLElement = fixture.nativeElement;
71
+ const webComponentHtml = html.querySelector('descope-wc')!;
72
+ expect(webComponentHtml.getAttribute('project-id')).toStrictEqual('123');
73
+ expect(webComponentHtml.getAttribute('flow-id')).toStrictEqual('sign-in');
74
+ expect(webComponentHtml.getAttribute('locale')).toStrictEqual('en-US');
75
+ expect(webComponentHtml.getAttribute('logger')).toBeDefined();
76
+ expect(webComponentHtml.getAttribute('error-transformer')).toBeDefined();
77
+ expect(webComponentHtml.getAttribute('redirect-url')).toBeNull();
78
+ });
79
+
80
+ it('should emit success when web component emits success', () => {
81
+ const html: HTMLElement = fixture.nativeElement;
82
+ const webComponentHtml = html.querySelector('descope-wc')!;
83
+
84
+ component.success.subscribe(() => {
85
+ expect(true).toBeTruthy();
86
+ expect(afterRequestHooksSpy).toHaveBeenCalled();
87
+ });
88
+ webComponentHtml.dispatchEvent(
89
+ new CustomEvent('success', {
90
+ detail: { user: { name: 'user1' }, sessionJwt: 'session1' }
91
+ })
92
+ );
93
+ });
94
+
95
+ it('should emit error when web component emits error', () => {
96
+ const html: HTMLElement = fixture.nativeElement;
97
+ const webComponentHtml = html.querySelector('descope-wc')!;
98
+
99
+ component.error.subscribe(() => {
100
+ expect(true).toBeTruthy();
101
+ });
102
+ webComponentHtml.dispatchEvent(new CustomEvent('error'));
103
+ });
104
+ });
@@ -0,0 +1,114 @@
1
+ import {
2
+ Component,
3
+ ElementRef,
4
+ EventEmitter,
5
+ Input,
6
+ OnChanges,
7
+ OnInit,
8
+ Output
9
+ } from '@angular/core';
10
+ import DescopeWebComponent from '@descope/web-component';
11
+ import DescopeWc, { ILogger } from '@descope/web-component';
12
+ import { DescopeAuthService } from '../../services/descope-auth.service';
13
+ import { from } from 'rxjs';
14
+ import { baseHeaders } from '../../utils/constants';
15
+ import { DescopeAuthConfig } from '../../types/types';
16
+
17
+ @Component({
18
+ selector: 'descope[flowId]',
19
+ standalone: true,
20
+ template: ''
21
+ })
22
+ export class DescopeComponent implements OnInit, OnChanges {
23
+ projectId: string;
24
+ @Input() flowId: string;
25
+
26
+ @Input() locale: string;
27
+ @Input() theme: 'light' | 'dark' | 'os';
28
+ @Input() tenant: string;
29
+ @Input() telemetryKey: string;
30
+ @Input() redirectUrl: string;
31
+ @Input() autoFocus: true | false | 'skipFirstScreen';
32
+
33
+ @Input() debug: boolean;
34
+ @Input() errorTransformer: (error: { text: string; type: string }) => string;
35
+ @Input() logger: ILogger;
36
+
37
+ @Output() success: EventEmitter<void> = new EventEmitter<void>();
38
+ @Output() error: EventEmitter<void> = new EventEmitter<void>();
39
+
40
+ private readonly webComponent: DescopeWebComponent =
41
+ new DescopeWebComponent();
42
+
43
+ constructor(
44
+ private elementRef: ElementRef,
45
+ private authService: DescopeAuthService,
46
+ descopeConfig: DescopeAuthConfig
47
+ ) {
48
+ this.projectId = descopeConfig.projectId;
49
+ }
50
+
51
+ ngOnInit() {
52
+ DescopeWc.sdkConfigOverrides = { baseHeaders };
53
+ this.setupWebComponent();
54
+ this.elementRef.nativeElement.appendChild(this.webComponent);
55
+ }
56
+
57
+ ngOnChanges(): void {
58
+ this.setupWebComponent();
59
+ }
60
+
61
+ private setupWebComponent() {
62
+ this.webComponent.setAttribute('project-id', this.projectId);
63
+ this.webComponent.setAttribute('flow-id', this.flowId);
64
+ if (this.locale) {
65
+ this.webComponent.setAttribute('locale', this.locale);
66
+ }
67
+ if (this.theme) {
68
+ this.webComponent.setAttribute('theme', this.theme);
69
+ }
70
+ if (this.tenant) {
71
+ this.webComponent.setAttribute('tenant', this.tenant);
72
+ }
73
+ if (this.telemetryKey) {
74
+ this.webComponent.setAttribute('telemetryKey', this.telemetryKey);
75
+ }
76
+ if (this.redirectUrl) {
77
+ this.webComponent.setAttribute('redirect-url', this.redirectUrl);
78
+ }
79
+ if (this.autoFocus) {
80
+ this.webComponent.setAttribute('auto-focus', this.autoFocus.toString());
81
+ }
82
+ if (this.debug) {
83
+ this.webComponent.setAttribute('debug', this.debug.toString());
84
+ }
85
+
86
+ if (this.errorTransformer) {
87
+ this.webComponent.errorTransformer = this.errorTransformer;
88
+ }
89
+
90
+ if (this.logger) {
91
+ this.webComponent.logger = this.logger;
92
+ }
93
+
94
+ if (this.success) {
95
+ this.webComponent.addEventListener('success', (e: Event) => {
96
+ from(
97
+ this.authService.descopeSdk.httpClient.hooks?.afterRequest!(
98
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
+ {} as any,
100
+ new Response(JSON.stringify((e as CustomEvent).detail))
101
+ ) as Promise<unknown>
102
+ ).subscribe(() => {
103
+ this.success?.emit();
104
+ });
105
+ });
106
+ }
107
+
108
+ if (this.error) {
109
+ this.webComponent.addEventListener('error', () => {
110
+ this.error?.emit();
111
+ });
112
+ }
113
+ }
114
+ }
@@ -0,0 +1,15 @@
1
+ <descope
2
+ (success)="success.emit()"
3
+ (error)="error.emit()"
4
+ flowId="sign-in"
5
+ [locale]="locale"
6
+ [theme]="theme"
7
+ [tenant]="tenant"
8
+ [telemetryKey]="telemetryKey"
9
+ [redirectUrl]="redirectUrl"
10
+ [autoFocus]="autoFocus"
11
+ [debug]="debug"
12
+ [errorTransformer]="errorTransformer"
13
+ [logger]="logger"
14
+ >
15
+ </descope>
@@ -0,0 +1,53 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { SignInFlowComponent } from './sign-in-flow.component';
3
+ import { ngMocks } from 'ng-mocks';
4
+ import { DescopeComponent } from '../descope/descope.component';
5
+ import { DescopeAuthConfig } from '../../types/types';
6
+ import createSdk from '@descope/web-js-sdk';
7
+ import mocked = jest.mocked;
8
+
9
+ jest.mock('@descope/web-js-sdk');
10
+ jest.mock('@descope/web-component', () => {
11
+ return jest.fn(() => {
12
+ // Create a mock DOM element
13
+ return document.createElement('descope-wc');
14
+ });
15
+ });
16
+
17
+ describe('SignInFlowComponent', () => {
18
+ let component: SignInFlowComponent;
19
+ let fixture: ComponentFixture<SignInFlowComponent>;
20
+ let mockedCreateSdk: jest.Mock;
21
+
22
+ beforeEach(() => {
23
+ mockedCreateSdk = mocked(createSdk);
24
+
25
+ mockedCreateSdk.mockReturnValue({
26
+ onSessionTokenChange: jest.fn(),
27
+ onUserChange: jest.fn()
28
+ });
29
+
30
+ TestBed.configureTestingModule({
31
+ providers: [
32
+ DescopeAuthConfig,
33
+ {
34
+ provide: DescopeAuthConfig,
35
+ useValue: {
36
+ projectId: 'someProject'
37
+ }
38
+ }
39
+ ]
40
+ });
41
+
42
+ fixture = TestBed.createComponent(SignInFlowComponent);
43
+ component = fixture.componentInstance;
44
+ fixture.detectChanges();
45
+ });
46
+
47
+ it('should create and be correctly configured', () => {
48
+ expect(component).toBeTruthy();
49
+ const mockComponent =
50
+ ngMocks.find<DescopeComponent>('[flowId=sign-in]').componentInstance;
51
+ expect(mockComponent.flowId).toStrictEqual('sign-in');
52
+ });
53
+ });
@@ -0,0 +1,32 @@
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { ILogger } from '@descope/web-component';
3
+ import { DescopeComponent } from '../descope/descope.component';
4
+ import { DescopeAuthConfig } from '../../types/types';
5
+
6
+ @Component({
7
+ selector: 'descope-sign-in-flow',
8
+ standalone: true,
9
+ imports: [DescopeComponent],
10
+ templateUrl: './sign-in-flow.component.html'
11
+ })
12
+ export class SignInFlowComponent {
13
+ projectId: string;
14
+
15
+ @Input() locale: string;
16
+ @Input() theme: 'light' | 'dark' | 'os';
17
+ @Input() tenant: string;
18
+ @Input() telemetryKey: string;
19
+ @Input() redirectUrl: string;
20
+ @Input() autoFocus: true | false | 'skipFirstScreen';
21
+
22
+ @Input() debug: boolean;
23
+ @Input() errorTransformer: (error: { text: string; type: string }) => string;
24
+ @Input() logger: ILogger;
25
+
26
+ @Output() success: EventEmitter<void> = new EventEmitter<void>();
27
+ @Output() error: EventEmitter<void> = new EventEmitter<void>();
28
+
29
+ constructor(descopeConfig: DescopeAuthConfig) {
30
+ this.projectId = descopeConfig.projectId;
31
+ }
32
+ }
@@ -0,0 +1,15 @@
1
+ <descope
2
+ (success)="success.emit()"
3
+ (error)="error.emit()"
4
+ flowId="sign-up"
5
+ [locale]="locale"
6
+ [theme]="theme"
7
+ [tenant]="tenant"
8
+ [telemetryKey]="telemetryKey"
9
+ [redirectUrl]="redirectUrl"
10
+ [autoFocus]="autoFocus"
11
+ [debug]="debug"
12
+ [errorTransformer]="errorTransformer"
13
+ [logger]="logger"
14
+ >
15
+ </descope>
@@ -0,0 +1,51 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { SignUpFlowComponent } from './sign-up-flow.component';
3
+ import { DescopeComponent } from '../descope/descope.component';
4
+ import { ngMocks } from 'ng-mocks';
5
+ import createSdk from '@descope/web-js-sdk';
6
+ import { DescopeAuthConfig } from '../../types/types';
7
+ import mocked = jest.mocked;
8
+
9
+ jest.mock('@descope/web-js-sdk');
10
+ jest.mock('@descope/web-component', () => {
11
+ return jest.fn(() => {
12
+ // Create a mock DOM element
13
+ return document.createElement('descope-wc');
14
+ });
15
+ });
16
+ describe('SignUpFlowComponent', () => {
17
+ let component: SignUpFlowComponent;
18
+ let fixture: ComponentFixture<SignUpFlowComponent>;
19
+ let mockedCreateSdk: jest.Mock;
20
+
21
+ beforeEach(() => {
22
+ mockedCreateSdk = mocked(createSdk);
23
+
24
+ mockedCreateSdk.mockReturnValue({
25
+ onSessionTokenChange: jest.fn(),
26
+ onUserChange: jest.fn()
27
+ });
28
+
29
+ TestBed.configureTestingModule({
30
+ providers: [
31
+ DescopeAuthConfig,
32
+ {
33
+ provide: DescopeAuthConfig,
34
+ useValue: {
35
+ projectId: 'someProject'
36
+ }
37
+ }
38
+ ]
39
+ });
40
+ fixture = TestBed.createComponent(SignUpFlowComponent);
41
+ component = fixture.componentInstance;
42
+ fixture.detectChanges();
43
+ });
44
+
45
+ it('should create and be correctly configured', () => {
46
+ expect(component).toBeTruthy();
47
+ const mockComponent =
48
+ ngMocks.find<DescopeComponent>('[flowId=sign-up]').componentInstance;
49
+ expect(mockComponent.flowId).toStrictEqual('sign-up');
50
+ });
51
+ });
@@ -0,0 +1,32 @@
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { ILogger } from '@descope/web-component';
3
+ import { DescopeComponent } from '../descope/descope.component';
4
+ import { DescopeAuthConfig } from '../../types/types';
5
+
6
+ @Component({
7
+ selector: 'descope-sign-up-flow',
8
+ standalone: true,
9
+ imports: [DescopeComponent],
10
+ templateUrl: './sign-up-flow.component.html'
11
+ })
12
+ export class SignUpFlowComponent {
13
+ projectId: string;
14
+
15
+ @Input() locale: string;
16
+ @Input() theme: 'light' | 'dark' | 'os';
17
+ @Input() tenant: string;
18
+ @Input() telemetryKey: string;
19
+ @Input() redirectUrl: string;
20
+ @Input() autoFocus: true | false | 'skipFirstScreen';
21
+
22
+ @Input() debug: boolean;
23
+ @Input() errorTransformer: (error: { text: string; type: string }) => string;
24
+ @Input() logger: ILogger;
25
+
26
+ @Output() success: EventEmitter<void> = new EventEmitter<void>();
27
+ @Output() error: EventEmitter<void> = new EventEmitter<void>();
28
+
29
+ constructor(descopeConfig: DescopeAuthConfig) {
30
+ this.projectId = descopeConfig.projectId;
31
+ }
32
+ }
@@ -0,0 +1,15 @@
1
+ <descope
2
+ (success)="success.emit()"
3
+ (error)="error.emit()"
4
+ flowId="sign-up-or-in"
5
+ [locale]="locale"
6
+ [theme]="theme"
7
+ [tenant]="tenant"
8
+ [telemetryKey]="telemetryKey"
9
+ [redirectUrl]="redirectUrl"
10
+ [autoFocus]="autoFocus"
11
+ [debug]="debug"
12
+ [errorTransformer]="errorTransformer"
13
+ [logger]="logger"
14
+ >
15
+ </descope>
@@ -0,0 +1,53 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { SignUpOrInFlowComponent } from './sign-up-or-in-flow.component';
3
+ import { DescopeComponent } from '../descope/descope.component';
4
+ import { ngMocks } from 'ng-mocks';
5
+ import createSdk from '@descope/web-js-sdk';
6
+ import { DescopeAuthConfig } from '../../types/types';
7
+ import mocked = jest.mocked;
8
+
9
+ jest.mock('@descope/web-js-sdk');
10
+ jest.mock('@descope/web-component', () => {
11
+ return jest.fn(() => {
12
+ // Create a mock DOM element
13
+ return document.createElement('descope-wc');
14
+ });
15
+ });
16
+ describe('SignUpOrInFlowComponent', () => {
17
+ let component: SignUpOrInFlowComponent;
18
+ let fixture: ComponentFixture<SignUpOrInFlowComponent>;
19
+ let mockedCreateSdk: jest.Mock;
20
+
21
+ beforeEach(() => {
22
+ mockedCreateSdk = mocked(createSdk);
23
+
24
+ mockedCreateSdk.mockReturnValue({
25
+ onSessionTokenChange: jest.fn(),
26
+ onUserChange: jest.fn()
27
+ });
28
+
29
+ TestBed.configureTestingModule({
30
+ providers: [
31
+ DescopeAuthConfig,
32
+ {
33
+ provide: DescopeAuthConfig,
34
+ useValue: {
35
+ projectId: 'someProject'
36
+ }
37
+ }
38
+ ]
39
+ });
40
+
41
+ fixture = TestBed.createComponent(SignUpOrInFlowComponent);
42
+ component = fixture.componentInstance;
43
+ fixture.detectChanges();
44
+ });
45
+
46
+ it('should create and be correctly configured', () => {
47
+ expect(component).toBeTruthy();
48
+ const mockComponent = ngMocks.find<DescopeComponent>(
49
+ '[flowId=sign-up-or-in]'
50
+ ).componentInstance;
51
+ expect(mockComponent.flowId).toStrictEqual('sign-up-or-in');
52
+ });
53
+ });
@@ -0,0 +1,32 @@
1
+ import { Component, EventEmitter, Input, Output } from '@angular/core';
2
+ import { ILogger } from '@descope/web-component';
3
+ import { DescopeComponent } from '../descope/descope.component';
4
+ import { DescopeAuthConfig } from '../../types/types';
5
+
6
+ @Component({
7
+ selector: 'descope-sign-up-or-in-flow',
8
+ standalone: true,
9
+ imports: [DescopeComponent],
10
+ templateUrl: './sign-up-or-in-flow.component.html'
11
+ })
12
+ export class SignUpOrInFlowComponent {
13
+ projectId: string;
14
+
15
+ @Input() locale: string;
16
+ @Input() theme: 'light' | 'dark' | 'os';
17
+ @Input() tenant: string;
18
+ @Input() telemetryKey: string;
19
+ @Input() redirectUrl: string;
20
+ @Input() autoFocus: true | false | 'skipFirstScreen';
21
+
22
+ @Input() debug: boolean;
23
+ @Input() errorTransformer: (error: { text: string; type: string }) => string;
24
+ @Input() logger: ILogger;
25
+
26
+ @Output() success: EventEmitter<void> = new EventEmitter<void>();
27
+ @Output() error: EventEmitter<void> = new EventEmitter<void>();
28
+
29
+ constructor(descopeConfig: DescopeAuthConfig) {
30
+ this.projectId = descopeConfig.projectId;
31
+ }
32
+ }
@@ -0,0 +1,46 @@
1
+ import {
2
+ CUSTOM_ELEMENTS_SCHEMA,
3
+ ModuleWithProviders,
4
+ NgModule,
5
+ Optional,
6
+ SkipSelf
7
+ } from '@angular/core';
8
+ import { DescopeComponent } from './components/descope/descope.component';
9
+ import { SignInFlowComponent } from './components/sign-in-flow/sign-in-flow.component';
10
+ import { SignUpFlowComponent } from './components/sign-up-flow/sign-up-flow.component';
11
+ import { SignUpOrInFlowComponent } from './components/sign-up-or-in-flow/sign-up-or-in-flow.component';
12
+ import { DescopeAuthConfig } from './types/types';
13
+
14
+ @NgModule({
15
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
16
+ imports: [
17
+ DescopeComponent,
18
+ SignInFlowComponent,
19
+ SignUpFlowComponent,
20
+ SignUpOrInFlowComponent
21
+ ],
22
+ exports: [
23
+ DescopeComponent,
24
+ SignInFlowComponent,
25
+ SignUpFlowComponent,
26
+ SignUpOrInFlowComponent
27
+ ]
28
+ })
29
+ export class DescopeAuthModule {
30
+ constructor(@Optional() @SkipSelf() parentModule?: DescopeAuthModule) {
31
+ if (parentModule) {
32
+ throw new Error(
33
+ 'DescopeAuthModule is already loaded. Import it only once'
34
+ );
35
+ }
36
+ }
37
+
38
+ static forRoot(
39
+ config?: DescopeAuthConfig
40
+ ): ModuleWithProviders<DescopeAuthModule> {
41
+ return {
42
+ ngModule: DescopeAuthModule,
43
+ providers: [{ provide: DescopeAuthConfig, useValue: config }]
44
+ };
45
+ }
46
+ }
@@ -0,0 +1,76 @@
1
+ import { delay, Observable } from 'rxjs';
2
+ import { ActivatedRoute, Router } from '@angular/router';
3
+ import { descopeAuthGuard } from './descope-auth.guard';
4
+ import { fakeAsync, TestBed, tick } from '@angular/core/testing';
5
+ import { RouterTestingModule } from '@angular/router/testing';
6
+ import { DescopeAuthService } from './descope-auth.service';
7
+
8
+ describe('descopeAuthGuard', () => {
9
+ const authServiceMock = {
10
+ isAuthenticated: jest.fn()
11
+ };
12
+
13
+ const routerMock = {
14
+ navigate: jest.fn()
15
+ };
16
+
17
+ beforeEach(async () => {
18
+ await TestBed.configureTestingModule({
19
+ imports: [RouterTestingModule],
20
+ providers: [
21
+ {
22
+ provide: Router,
23
+ useValue: routerMock
24
+ },
25
+ {
26
+ provide: DescopeAuthService,
27
+ useValue: authServiceMock
28
+ },
29
+ {
30
+ provide: ActivatedRoute,
31
+ useValue: {
32
+ snapshot: {
33
+ data: {
34
+ descopeFallbackUrl: '/fallback'
35
+ }
36
+ }
37
+ }
38
+ }
39
+ ]
40
+ });
41
+ });
42
+
43
+ it('return true if authenticated', fakeAsync(() => {
44
+ const activatedRoute = TestBed.inject(ActivatedRoute);
45
+ authServiceMock.isAuthenticated.mockReturnValue(true);
46
+ const guardResponse = TestBed.runInInjectionContext(() => {
47
+ return descopeAuthGuard(activatedRoute.snapshot) as Observable<boolean>;
48
+ });
49
+
50
+ let guardOutput = null;
51
+ guardResponse
52
+ .pipe(delay(100))
53
+ .subscribe((response) => (guardOutput = response));
54
+ tick(100);
55
+
56
+ expect(guardOutput).toBeTruthy();
57
+ }));
58
+
59
+ it('navigate to fallbackUrl when not auth', fakeAsync(() => {
60
+ const activatedRoute = TestBed.inject(ActivatedRoute);
61
+ authServiceMock.isAuthenticated.mockReturnValue(false);
62
+ routerMock.navigate.mockReturnValue([]);
63
+ const guardResponse = TestBed.runInInjectionContext(() => {
64
+ return descopeAuthGuard(activatedRoute.snapshot) as Observable<boolean>;
65
+ });
66
+
67
+ let guardOutput = null;
68
+ guardResponse
69
+ .pipe(delay(100))
70
+ .subscribe((response) => (guardOutput = response));
71
+ tick(100);
72
+
73
+ expect(guardOutput).toBeFalsy();
74
+ expect(routerMock.navigate).toHaveBeenCalledWith(['/fallback']);
75
+ }));
76
+ });
@@ -0,0 +1,16 @@
1
+ import { inject } from '@angular/core';
2
+
3
+ import { DescopeAuthService } from './descope-auth.service';
4
+ import { ActivatedRouteSnapshot, Router } from '@angular/router';
5
+ import { from, of } from 'rxjs';
6
+
7
+ export const descopeAuthGuard = (route: ActivatedRouteSnapshot) => {
8
+ const authService = inject(DescopeAuthService);
9
+ const router = inject(Router);
10
+ const fallbackUrl = route.data['descopeFallbackUrl'];
11
+ const isAuthenticated = authService.isAuthenticated();
12
+ if (!isAuthenticated && !!fallbackUrl) {
13
+ return from(router.navigate([fallbackUrl]));
14
+ }
15
+ return of(isAuthenticated);
16
+ };