@descope/angular-sdk 0.0.0-next-7be68a9b-20231226 → 0.0.1
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.
- package/README.md +13 -448
- package/environment.d.ts +3 -0
- package/esm2022/descope-angular-sdk.mjs +5 -0
- package/esm2022/environment.mjs +4 -0
- package/esm2022/lib/components/descope/descope.component.mjs +106 -0
- package/esm2022/lib/components/sign-in-flow/sign-in-flow.component.mjs +40 -0
- package/esm2022/lib/components/sign-up-flow/sign-up-flow.component.mjs +40 -0
- package/esm2022/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.mjs +40 -0
- package/esm2022/lib/descope-auth.module.mjs +52 -0
- package/esm2022/lib/services/descope-auth.guard.mjs +15 -0
- package/esm2022/lib/services/descope-auth.service.mjs +139 -0
- package/esm2022/lib/services/descope.interceptor.mjs +51 -0
- package/esm2022/lib/types/types.mjs +6 -0
- package/esm2022/lib/utils/constants.mjs +7 -0
- package/esm2022/lib/utils/helpers.mjs +27 -0
- package/esm2022/public-api.mjs +13 -0
- package/fesm2022/descope-angular-sdk.mjs +500 -0
- package/fesm2022/descope-angular-sdk.mjs.map +1 -0
- package/index.d.ts +5 -0
- package/lib/components/descope/descope.component.d.ts +32 -0
- package/lib/components/sign-in-flow/sign-in-flow.component.d.ts +24 -0
- package/lib/components/sign-up-flow/sign-up-flow.component.d.ts +24 -0
- package/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.d.ts +24 -0
- package/lib/descope-auth.module.d.ts +14 -0
- package/lib/services/descope-auth.guard.d.ts +2 -0
- package/lib/services/descope-auth.service.d.ts +38 -0
- package/lib/services/descope.interceptor.d.ts +2 -0
- package/lib/types/types.d.ts +8 -0
- package/lib/utils/constants.d.ts +5 -0
- package/lib/utils/helpers.d.ts +5 -0
- package/package.json +32 -65
- package/{projects/angular-sdk/src/public-api.ts → public-api.d.ts} +0 -4
- package/.editorconfig +0 -16
- package/.eslintrc.json +0 -40
- package/.github/workflows/ci.yml +0 -99
- package/.github/workflows/publish-next.yml +0 -40
- package/.github/workflows/publish.yml +0 -28
- package/.github/workflows/release.yml +0 -46
- package/.husky/pre-commit +0 -4
- package/.prettierrc +0 -8
- package/.vscode/extensions.json +0 -4
- package/.vscode/launch.json +0 -20
- package/.vscode/settings.json +0 -3
- package/.vscode/tasks.json +0 -42
- package/LICENSE +0 -21
- package/angular.json +0 -154
- package/jest.config.js +0 -17
- package/projects/angular-sdk/.eslintrc.json +0 -32
- package/projects/angular-sdk/README.md +0 -25
- package/projects/angular-sdk/ng-package.json +0 -8
- package/projects/angular-sdk/package.json +0 -20
- package/projects/angular-sdk/src/environment.ts +0 -3
- package/projects/angular-sdk/src/lib/components/descope/descope.component.spec.ts +0 -104
- package/projects/angular-sdk/src/lib/components/descope/descope.component.ts +0 -114
- package/projects/angular-sdk/src/lib/components/sign-in-flow/sign-in-flow.component.html +0 -15
- package/projects/angular-sdk/src/lib/components/sign-in-flow/sign-in-flow.component.spec.ts +0 -53
- package/projects/angular-sdk/src/lib/components/sign-in-flow/sign-in-flow.component.ts +0 -32
- package/projects/angular-sdk/src/lib/components/sign-up-flow/sign-up-flow.component.html +0 -15
- package/projects/angular-sdk/src/lib/components/sign-up-flow/sign-up-flow.component.spec.ts +0 -51
- package/projects/angular-sdk/src/lib/components/sign-up-flow/sign-up-flow.component.ts +0 -32
- package/projects/angular-sdk/src/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.html +0 -15
- package/projects/angular-sdk/src/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.spec.ts +0 -53
- package/projects/angular-sdk/src/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.ts +0 -32
- package/projects/angular-sdk/src/lib/descope-auth.module.ts +0 -46
- package/projects/angular-sdk/src/lib/services/descope-auth.guard.spec.ts +0 -76
- package/projects/angular-sdk/src/lib/services/descope-auth.guard.ts +0 -16
- package/projects/angular-sdk/src/lib/services/descope-auth.service.spec.ts +0 -264
- package/projects/angular-sdk/src/lib/services/descope-auth.service.ts +0 -176
- package/projects/angular-sdk/src/lib/services/descope.interceptor.spec.ts +0 -102
- package/projects/angular-sdk/src/lib/services/descope.interceptor.ts +0 -76
- package/projects/angular-sdk/src/lib/types/types.ts +0 -10
- package/projects/angular-sdk/src/lib/utils/constants.ts +0 -8
- package/projects/angular-sdk/src/lib/utils/helpers.spec.ts +0 -103
- package/projects/angular-sdk/src/lib/utils/helpers.ts +0 -36
- package/projects/angular-sdk/tsconfig.lib.json +0 -12
- package/projects/angular-sdk/tsconfig.lib.prod.json +0 -10
- package/projects/angular-sdk/tsconfig.spec.json +0 -11
- package/projects/demo-app/.eslintrc.json +0 -31
- package/projects/demo-app/src/app/app-routing.module.ts +0 -23
- package/projects/demo-app/src/app/app.component.html +0 -3
- package/projects/demo-app/src/app/app.component.scss +0 -16
- package/projects/demo-app/src/app/app.component.spec.ts +0 -37
- package/projects/demo-app/src/app/app.component.ts +0 -8
- package/projects/demo-app/src/app/app.module.ts +0 -52
- package/projects/demo-app/src/app/home/home.component.html +0 -23
- package/projects/demo-app/src/app/home/home.component.scss +0 -15
- package/projects/demo-app/src/app/home/home.component.spec.ts +0 -44
- package/projects/demo-app/src/app/home/home.component.ts +0 -61
- package/projects/demo-app/src/app/interceptor/auth.interceptor.ts +0 -20
- package/projects/demo-app/src/app/login/login.component.html +0 -12
- package/projects/demo-app/src/app/login/login.component.spec.ts +0 -42
- package/projects/demo-app/src/app/login/login.component.ts +0 -35
- package/projects/demo-app/src/app/protected/protected.component.html +0 -18
- package/projects/demo-app/src/app/protected/protected.component.scss +0 -8
- package/projects/demo-app/src/app/protected/protected.component.spec.ts +0 -42
- package/projects/demo-app/src/app/protected/protected.component.ts +0 -40
- package/projects/demo-app/src/assets/.gitkeep +0 -0
- package/projects/demo-app/src/environments/conifg.ts +0 -13
- package/projects/demo-app/src/environments/environment.ts +0 -19
- package/projects/demo-app/src/favicon.ico +0 -0
- package/projects/demo-app/src/index.html +0 -17
- package/projects/demo-app/src/main.ts +0 -7
- package/projects/demo-app/src/styles.scss +0 -21
- package/projects/demo-app/tsconfig.app.json +0 -10
- package/projects/demo-app/tsconfig.spec.json +0 -10
- package/renovate.json +0 -4
- package/scripts/gitleaks/.gitleaks.toml +0 -653
- package/scripts/gitleaks/gitleaks.sh +0 -34
- package/scripts/setversion/setversion.js +0 -20
- package/setup-jest.ts +0 -1
- package/thirdPartyLicenseCollector_linux_amd64 +0 -0
- package/tsconfig.json +0 -35
package/projects/angular-sdk/src/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.html
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
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>
|
|
@@ -1,53 +0,0 @@
|
|
|
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
|
-
});
|
package/projects/angular-sdk/src/lib/components/sign-up-or-in-flow/sign-up-or-in-flow.component.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
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
|
-
};
|
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import { TestBed } from '@angular/core/testing';
|
|
2
|
-
|
|
3
|
-
import { DescopeAuthService } from './descope-auth.service';
|
|
4
|
-
import createSdk from '@descope/web-js-sdk';
|
|
5
|
-
import mocked = jest.mocked;
|
|
6
|
-
import { DescopeAuthConfig } from '../types/types';
|
|
7
|
-
import { of, take, toArray } from 'rxjs';
|
|
8
|
-
|
|
9
|
-
jest.mock('@descope/web-js-sdk');
|
|
10
|
-
|
|
11
|
-
describe('DescopeAuthService', () => {
|
|
12
|
-
let service: DescopeAuthService;
|
|
13
|
-
let mockedCreateSdk: jest.Mock;
|
|
14
|
-
let windowSpy: jest.SpyInstance;
|
|
15
|
-
const onSessionTokenChangeSpy = jest.fn();
|
|
16
|
-
const onUserChangeSpy = jest.fn();
|
|
17
|
-
const getSessionTokenSpy = jest.fn();
|
|
18
|
-
const getRefreshTokenSpy = jest.fn();
|
|
19
|
-
const getJwtPermissionsSpy = jest.fn();
|
|
20
|
-
const getJwtRolesSpy = jest.fn();
|
|
21
|
-
const meSpy = jest.fn();
|
|
22
|
-
const refreshSpy = jest.fn();
|
|
23
|
-
const mockConfig: DescopeAuthConfig = {
|
|
24
|
-
projectId: 'someProject'
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
beforeEach(() => {
|
|
28
|
-
mockedCreateSdk = mocked(createSdk);
|
|
29
|
-
windowSpy = jest.spyOn(window, 'window', 'get');
|
|
30
|
-
|
|
31
|
-
mockedCreateSdk.mockReturnValue({
|
|
32
|
-
onSessionTokenChange: onSessionTokenChangeSpy,
|
|
33
|
-
onUserChange: onUserChangeSpy,
|
|
34
|
-
getSessionToken: getSessionTokenSpy,
|
|
35
|
-
getRefreshToken: getRefreshTokenSpy,
|
|
36
|
-
getJwtPermissions: getJwtPermissionsSpy,
|
|
37
|
-
getJwtRoles: getJwtRolesSpy,
|
|
38
|
-
me: meSpy,
|
|
39
|
-
refresh: refreshSpy
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
onSessionTokenChangeSpy.mockImplementation((fn) => fn());
|
|
43
|
-
onUserChangeSpy.mockImplementation((fn) => fn());
|
|
44
|
-
|
|
45
|
-
TestBed.configureTestingModule({
|
|
46
|
-
providers: [
|
|
47
|
-
DescopeAuthConfig,
|
|
48
|
-
{ provide: DescopeAuthConfig, useValue: mockConfig }
|
|
49
|
-
]
|
|
50
|
-
});
|
|
51
|
-
service = TestBed.inject(DescopeAuthService);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
afterEach(() => {
|
|
55
|
-
getSessionTokenSpy.mockReset();
|
|
56
|
-
getRefreshTokenSpy.mockReset();
|
|
57
|
-
getJwtPermissionsSpy.mockReset();
|
|
58
|
-
getJwtRolesSpy.mockReset();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should be created', () => {
|
|
62
|
-
expect(service).toBeTruthy();
|
|
63
|
-
expect(mockedCreateSdk).toHaveBeenCalledWith(
|
|
64
|
-
expect.objectContaining(mockConfig)
|
|
65
|
-
);
|
|
66
|
-
expect(onSessionTokenChangeSpy).toHaveBeenCalled();
|
|
67
|
-
expect(onUserChangeSpy).toHaveBeenCalled();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
describe('getSessionToken', () => {
|
|
71
|
-
it('should call getSessionToken from sdk', () => {
|
|
72
|
-
const token = 'abcd';
|
|
73
|
-
getSessionTokenSpy.mockReturnValueOnce(token);
|
|
74
|
-
const result = service.getSessionToken();
|
|
75
|
-
expect(getSessionTokenSpy).toHaveBeenCalled();
|
|
76
|
-
expect(result).toStrictEqual(token);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('should warn when using getSessionToken in non browser environment', () => {
|
|
80
|
-
const warnSpy = jest.spyOn(console, 'warn');
|
|
81
|
-
windowSpy.mockImplementationOnce(() => undefined);
|
|
82
|
-
|
|
83
|
-
service.getSessionToken();
|
|
84
|
-
|
|
85
|
-
expect(warnSpy).toHaveBeenCalledWith(
|
|
86
|
-
'Get session token is not supported in SSR'
|
|
87
|
-
);
|
|
88
|
-
expect(getSessionTokenSpy).not.toHaveBeenCalled();
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
describe('getRefreshToken', () => {
|
|
93
|
-
it('should call getRefreshToken from sdk', () => {
|
|
94
|
-
const token = 'abcd';
|
|
95
|
-
getRefreshTokenSpy.mockReturnValueOnce(token);
|
|
96
|
-
const result = service.getRefreshToken();
|
|
97
|
-
expect(getRefreshTokenSpy).toHaveBeenCalled();
|
|
98
|
-
expect(result).toStrictEqual(token);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('should warn when using getRefreshToken in non browser environment', () => {
|
|
102
|
-
const warnSpy = jest.spyOn(console, 'warn');
|
|
103
|
-
windowSpy.mockImplementationOnce(() => undefined);
|
|
104
|
-
|
|
105
|
-
service.getRefreshToken();
|
|
106
|
-
|
|
107
|
-
expect(warnSpy).toHaveBeenCalledWith(
|
|
108
|
-
'Get refresh token is not supported in SSR'
|
|
109
|
-
);
|
|
110
|
-
expect(getRefreshTokenSpy).not.toHaveBeenCalled();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
describe('getJwtPermissions', () => {
|
|
115
|
-
it('should return permissions for token from sdk', () => {
|
|
116
|
-
const permissions = ['edit'];
|
|
117
|
-
getJwtPermissionsSpy.mockReturnValueOnce(permissions);
|
|
118
|
-
const result = service.getJwtPermissions('token');
|
|
119
|
-
expect(getJwtPermissionsSpy).toHaveBeenCalledWith('token', undefined);
|
|
120
|
-
expect(result).toStrictEqual(permissions);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('should return empty array and log error when there is no token', () => {
|
|
124
|
-
const errorSpy = jest.spyOn(console, 'error');
|
|
125
|
-
getSessionTokenSpy.mockReturnValueOnce(null);
|
|
126
|
-
const result = service.getJwtPermissions();
|
|
127
|
-
expect(errorSpy).toHaveBeenCalledWith(
|
|
128
|
-
'Could not get JWT Permissions - not authenticated'
|
|
129
|
-
);
|
|
130
|
-
expect(getJwtPermissionsSpy).not.toHaveBeenCalled();
|
|
131
|
-
expect(result).toStrictEqual([]);
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
describe('getJwtRoles', () => {
|
|
136
|
-
it('should return roles for token from sdk', () => {
|
|
137
|
-
const roles = ['admin'];
|
|
138
|
-
getJwtRolesSpy.mockReturnValueOnce(roles);
|
|
139
|
-
const result = service.getJwtRoles('token');
|
|
140
|
-
expect(getJwtRolesSpy).toHaveBeenCalledWith('token', undefined);
|
|
141
|
-
expect(result).toStrictEqual(roles);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('should return empty array and log error when there is no token', () => {
|
|
145
|
-
const errorSpy = jest.spyOn(console, 'error');
|
|
146
|
-
getSessionTokenSpy.mockReturnValueOnce(null);
|
|
147
|
-
const result = service.getJwtRoles();
|
|
148
|
-
expect(errorSpy).toHaveBeenCalledWith(
|
|
149
|
-
'Could not get JWT Roles - not authenticated'
|
|
150
|
-
);
|
|
151
|
-
expect(getJwtRolesSpy).not.toHaveBeenCalled();
|
|
152
|
-
expect(result).toStrictEqual([]);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
describe('refreshSession', () => {
|
|
157
|
-
it('correctly handle descopeSession stream when session is successfully refreshed', (done: jest.DoneCallback) => {
|
|
158
|
-
refreshSpy.mockReturnValueOnce(
|
|
159
|
-
of({ ok: true, data: { sessionJwt: 'newToken' } })
|
|
160
|
-
);
|
|
161
|
-
// Taking 4 values from stream: first is initial value, next 3 are the result of refreshSession
|
|
162
|
-
service.session$.pipe(take(4), toArray()).subscribe({
|
|
163
|
-
next: (result) => {
|
|
164
|
-
expect(result.slice(1)).toStrictEqual([
|
|
165
|
-
{
|
|
166
|
-
isAuthenticated: false,
|
|
167
|
-
isSessionLoading: true,
|
|
168
|
-
sessionToken: undefined
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
isAuthenticated: true,
|
|
172
|
-
isSessionLoading: true,
|
|
173
|
-
sessionToken: 'newToken'
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
isAuthenticated: true,
|
|
177
|
-
isSessionLoading: false,
|
|
178
|
-
sessionToken: 'newToken'
|
|
179
|
-
}
|
|
180
|
-
]);
|
|
181
|
-
expect(service.isAuthenticated()).toBeTruthy();
|
|
182
|
-
done();
|
|
183
|
-
},
|
|
184
|
-
error: (err) => {
|
|
185
|
-
done.fail(err);
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
service.refreshSession().subscribe();
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
it('correctly handle descopeSession stream when refresh session failed', (done: jest.DoneCallback) => {
|
|
192
|
-
refreshSpy.mockReturnValueOnce(
|
|
193
|
-
of({ ok: false, data: { sessionJwt: 'newToken' } })
|
|
194
|
-
);
|
|
195
|
-
// Taking 4 values from stream: first is initial value, next 3 are the result of refreshSession
|
|
196
|
-
service.session$.pipe(take(4), toArray()).subscribe({
|
|
197
|
-
next: (result) => {
|
|
198
|
-
expect(result.slice(1)).toStrictEqual([
|
|
199
|
-
{
|
|
200
|
-
isAuthenticated: false,
|
|
201
|
-
isSessionLoading: true,
|
|
202
|
-
sessionToken: undefined
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
isAuthenticated: false,
|
|
206
|
-
isSessionLoading: true,
|
|
207
|
-
sessionToken: ''
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
isAuthenticated: false,
|
|
211
|
-
isSessionLoading: false,
|
|
212
|
-
sessionToken: ''
|
|
213
|
-
}
|
|
214
|
-
]);
|
|
215
|
-
expect(service.isAuthenticated()).toBeFalsy();
|
|
216
|
-
done();
|
|
217
|
-
},
|
|
218
|
-
error: (err) => {
|
|
219
|
-
done.fail(err);
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
service.refreshSession().subscribe();
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
describe('refreshUser', () => {
|
|
227
|
-
it('correctly handle descopeUser stream when user is successfully refreshed', (done: jest.DoneCallback) => {
|
|
228
|
-
meSpy.mockReturnValueOnce(of({ ok: true, data: { name: 'test' } }));
|
|
229
|
-
// Taking 4 values from stream: first is initial value, next 3 are the result of refreshUser
|
|
230
|
-
service.user$.pipe(take(4), toArray()).subscribe({
|
|
231
|
-
next: (result) => {
|
|
232
|
-
expect(result.slice(1)).toStrictEqual([
|
|
233
|
-
{ isUserLoading: true, user: undefined },
|
|
234
|
-
{ isUserLoading: true, user: { name: 'test' } },
|
|
235
|
-
{ isUserLoading: false, user: { name: 'test' } }
|
|
236
|
-
]);
|
|
237
|
-
done();
|
|
238
|
-
},
|
|
239
|
-
error: (err) => {
|
|
240
|
-
done.fail(err);
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
service.refreshUser().subscribe();
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
it('correctly handle descopeUser stream when refresh session failed', (done: jest.DoneCallback) => {
|
|
247
|
-
meSpy.mockReturnValueOnce(of({ ok: false }));
|
|
248
|
-
// Taking 3 values from stream: first is initial value, next 2 are the result of refreshUser
|
|
249
|
-
service.user$.pipe(take(3), toArray()).subscribe({
|
|
250
|
-
next: (result) => {
|
|
251
|
-
expect(result.slice(1)).toStrictEqual([
|
|
252
|
-
{ isUserLoading: true, user: undefined },
|
|
253
|
-
{ isUserLoading: false, user: undefined }
|
|
254
|
-
]);
|
|
255
|
-
done();
|
|
256
|
-
},
|
|
257
|
-
error: (err) => {
|
|
258
|
-
done.fail(err);
|
|
259
|
-
}
|
|
260
|
-
});
|
|
261
|
-
service.refreshUser().subscribe();
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import { Injectable } from '@angular/core';
|
|
2
|
-
import type { UserResponse } from '@descope/web-js-sdk';
|
|
3
|
-
import createSdk from '@descope/web-js-sdk';
|
|
4
|
-
import { BehaviorSubject, finalize, Observable, tap } from 'rxjs';
|
|
5
|
-
import { observabilify, Observablefied } from '../utils/helpers';
|
|
6
|
-
import { baseHeaders, isBrowser } from '../utils/constants';
|
|
7
|
-
import { DescopeAuthConfig } from '../types/types';
|
|
8
|
-
|
|
9
|
-
type DescopeSDK = ReturnType<typeof createSdk>;
|
|
10
|
-
type AngularDescopeSDK = Observablefied<DescopeSDK>;
|
|
11
|
-
|
|
12
|
-
export interface DescopeSession {
|
|
13
|
-
isAuthenticated: boolean;
|
|
14
|
-
isSessionLoading: boolean;
|
|
15
|
-
sessionToken: string | null;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type DescopeUser = { user?: UserResponse; isUserLoading: boolean };
|
|
19
|
-
|
|
20
|
-
@Injectable({
|
|
21
|
-
providedIn: 'root'
|
|
22
|
-
})
|
|
23
|
-
export class DescopeAuthService {
|
|
24
|
-
public descopeSdk: AngularDescopeSDK;
|
|
25
|
-
private readonly sessionSubject: BehaviorSubject<DescopeSession>;
|
|
26
|
-
private readonly userSubject: BehaviorSubject<DescopeUser>;
|
|
27
|
-
readonly session$: Observable<DescopeSession>;
|
|
28
|
-
readonly user$: Observable<DescopeUser>;
|
|
29
|
-
|
|
30
|
-
constructor(config: DescopeAuthConfig) {
|
|
31
|
-
this.descopeSdk = observabilify<DescopeSDK>(
|
|
32
|
-
createSdk({
|
|
33
|
-
...config,
|
|
34
|
-
persistTokens: isBrowser() as true,
|
|
35
|
-
autoRefresh: isBrowser() as true,
|
|
36
|
-
baseHeaders
|
|
37
|
-
})
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
this.sessionSubject = new BehaviorSubject<DescopeSession>({
|
|
41
|
-
isAuthenticated: false,
|
|
42
|
-
isSessionLoading: false,
|
|
43
|
-
sessionToken: ''
|
|
44
|
-
});
|
|
45
|
-
this.session$ = this.sessionSubject.asObservable();
|
|
46
|
-
this.userSubject = new BehaviorSubject<DescopeUser>({
|
|
47
|
-
isUserLoading: false
|
|
48
|
-
});
|
|
49
|
-
this.user$ = this.userSubject.asObservable();
|
|
50
|
-
this.descopeSdk.onSessionTokenChange(this.setSession.bind(this));
|
|
51
|
-
this.descopeSdk.onUserChange(this.setUser.bind(this));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
refreshSession() {
|
|
55
|
-
const beforeRefreshSession = this.sessionSubject.value;
|
|
56
|
-
this.sessionSubject.next({
|
|
57
|
-
...beforeRefreshSession,
|
|
58
|
-
isSessionLoading: true
|
|
59
|
-
});
|
|
60
|
-
return this.descopeSdk.refresh().pipe(
|
|
61
|
-
tap((data) => {
|
|
62
|
-
const afterRequestSession = this.sessionSubject.value;
|
|
63
|
-
if (data.ok && data.data) {
|
|
64
|
-
this.sessionSubject.next({
|
|
65
|
-
...afterRequestSession,
|
|
66
|
-
sessionToken: data.data.sessionJwt,
|
|
67
|
-
isAuthenticated: !!data.data.sessionJwt
|
|
68
|
-
});
|
|
69
|
-
} else {
|
|
70
|
-
this.sessionSubject.next({
|
|
71
|
-
...afterRequestSession,
|
|
72
|
-
sessionToken: '',
|
|
73
|
-
isAuthenticated: false
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
}),
|
|
77
|
-
finalize(() => {
|
|
78
|
-
const afterRefreshSession = this.sessionSubject.value;
|
|
79
|
-
this.sessionSubject.next({
|
|
80
|
-
...afterRefreshSession,
|
|
81
|
-
isSessionLoading: false
|
|
82
|
-
});
|
|
83
|
-
})
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
refreshUser() {
|
|
88
|
-
const beforeRefreshUser = this.userSubject.value;
|
|
89
|
-
this.userSubject.next({
|
|
90
|
-
...beforeRefreshUser,
|
|
91
|
-
isUserLoading: true
|
|
92
|
-
});
|
|
93
|
-
return this.descopeSdk.me().pipe(
|
|
94
|
-
tap((data) => {
|
|
95
|
-
const afterRequestUser = this.userSubject.value;
|
|
96
|
-
if (data.data) {
|
|
97
|
-
this.userSubject.next({
|
|
98
|
-
...afterRequestUser,
|
|
99
|
-
user: {
|
|
100
|
-
...data.data
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
}),
|
|
105
|
-
finalize(() => {
|
|
106
|
-
const afterRefreshUser = this.userSubject.value;
|
|
107
|
-
this.userSubject.next({
|
|
108
|
-
...afterRefreshUser,
|
|
109
|
-
isUserLoading: false
|
|
110
|
-
});
|
|
111
|
-
})
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
getSessionToken() {
|
|
116
|
-
if (isBrowser()) {
|
|
117
|
-
return (
|
|
118
|
-
this.descopeSdk as AngularDescopeSDK & {
|
|
119
|
-
getSessionToken: () => string | null;
|
|
120
|
-
}
|
|
121
|
-
).getSessionToken();
|
|
122
|
-
}
|
|
123
|
-
console.warn('Get session token is not supported in SSR');
|
|
124
|
-
return '';
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
getRefreshToken() {
|
|
128
|
-
if (isBrowser()) {
|
|
129
|
-
return (
|
|
130
|
-
this.descopeSdk as AngularDescopeSDK & {
|
|
131
|
-
getRefreshToken: () => string | null;
|
|
132
|
-
}
|
|
133
|
-
).getRefreshToken();
|
|
134
|
-
}
|
|
135
|
-
this.descopeSdk.getJwtPermissions;
|
|
136
|
-
console.warn('Get refresh token is not supported in SSR');
|
|
137
|
-
return '';
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
getJwtPermissions(token = this.getSessionToken(), tenant?: string) {
|
|
141
|
-
if (token === null) {
|
|
142
|
-
console.error('Could not get JWT Permissions - not authenticated');
|
|
143
|
-
return [];
|
|
144
|
-
}
|
|
145
|
-
return this.descopeSdk.getJwtPermissions(token, tenant);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
getJwtRoles(token = this.getSessionToken(), tenant?: string) {
|
|
149
|
-
if (token === null) {
|
|
150
|
-
console.error('Could not get JWT Roles - not authenticated');
|
|
151
|
-
return [];
|
|
152
|
-
}
|
|
153
|
-
return this.descopeSdk.getJwtRoles(token, tenant);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
isAuthenticated() {
|
|
157
|
-
return this.sessionSubject.value.isAuthenticated;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
private setSession(sessionToken: string | null) {
|
|
161
|
-
const currentSession = this.sessionSubject.value;
|
|
162
|
-
this.sessionSubject.next({
|
|
163
|
-
sessionToken,
|
|
164
|
-
isAuthenticated: !!sessionToken,
|
|
165
|
-
isSessionLoading: currentSession.isSessionLoading
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
private setUser(user: UserResponse) {
|
|
170
|
-
const currentUser = this.userSubject.value;
|
|
171
|
-
this.userSubject.next({
|
|
172
|
-
isUserLoading: currentUser.isUserLoading,
|
|
173
|
-
user
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
}
|