Skip to content

Commit d346f2d

Browse files
committed
AAE-37746 rename CrossAppAuthSyncService to CrossAppTokenManager and clear auth tokens when crossAppAuth=true to ensure correct user authentication
1 parent ec70efa commit d346f2d

10 files changed

+486
-553
lines changed

lib/core/src/lib/auth/oidc/auth.module.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ import { StorageService } from '../../common/services/storage.service';
2828
import { provideRouter } from '@angular/router';
2929
import { AUTH_ROUTES } from './auth.routes';
3030
import { Authentication, AuthenticationInterceptor } from '@alfresco/adf-core/auth';
31-
import { CrossAppAuthSyncService } from '../services/cross-app-auth-sync.service';
32-
import { CrossAppAuthIntegrationService } from '../services/cross-app-auth-integration.service';
31+
import { provideCrossAppAuth } from '../services/cross-app-auth.providers';
3332

3433
export const JWT_STORAGE_SERVICE = new InjectionToken<OAuthStorage>('JWT_STORAGE_SERVICE', {
3534
providedIn: 'root',
@@ -88,15 +87,7 @@ export class AuthModule {
8887
const providers: (Provider | EnvironmentProviders)[] = [{ provide: AUTH_MODULE_CONFIG, useValue: config }];
8988

9089
if (config.enableCrossAppSync) {
91-
providers.push(
92-
CrossAppAuthSyncService,
93-
CrossAppAuthIntegrationService,
94-
provideAppInitializer(() => {
95-
const crossAppIntegration = inject(CrossAppAuthIntegrationService);
96-
crossAppIntegration.initialize();
97-
return crossAppIntegration.attemptSilentLoginFromLinkedApps();
98-
})
99-
);
90+
providers.push(...provideCrossAppAuth());
10091
}
10192
return {
10293
ngModule: AuthModule,

lib/core/src/lib/auth/public-api.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ export * from './services/identity-group.service';
3030
export * from './services/jwt-helper.service';
3131
export * from './services/oauth2.service';
3232
export * from './services/user-access.service';
33-
export * from './services/cross-app-auth-sync.service';
33+
export * from './services/cross-app-token-manager.service';
3434
export * from './services/cross-app-auth-integration.service';
35+
export * from './services/cross-app-auth-initializer.service';
36+
export * from './services/cross-app-auth.providers';
3537

3638
export * from './basic-auth/basic-alfresco-auth.service';
3739
export * from './basic-auth/process-auth';
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*!
2+
* @license
3+
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { TestBed } from '@angular/core/testing';
19+
import { CrossAppAuthInitializerService } from './cross-app-auth-initializer.service';
20+
import { CrossAppAuthIntegrationService } from './cross-app-auth-integration.service';
21+
22+
describe('CrossAppAuthInitializerService', () => {
23+
let service: CrossAppAuthInitializerService;
24+
let mockCrossAppIntegration: jasmine.SpyObj<CrossAppAuthIntegrationService>;
25+
26+
beforeEach(() => {
27+
const spy = jasmine.createSpyObj('CrossAppAuthIntegrationService', ['initialize', 'processCrossAppAuthRequest']);
28+
29+
TestBed.configureTestingModule({
30+
providers: [CrossAppAuthInitializerService, { provide: CrossAppAuthIntegrationService, useValue: spy }]
31+
});
32+
33+
service = TestBed.inject(CrossAppAuthInitializerService);
34+
mockCrossAppIntegration = TestBed.inject(CrossAppAuthIntegrationService) as jasmine.SpyObj<CrossAppAuthIntegrationService>;
35+
});
36+
37+
it('should be created', () => {
38+
expect(service).toBeTruthy();
39+
});
40+
41+
describe('initializeWithSilentLogin', () => {
42+
it('should successfully initialize and attempt silent login', async () => {
43+
mockCrossAppIntegration.initialize.and.returnValue(Promise.resolve());
44+
mockCrossAppIntegration.processCrossAppAuthRequest.and.returnValue(Promise.resolve(true));
45+
46+
const result = await service.initializeCrossAppLogin();
47+
48+
expect(result).toBe(true);
49+
expect(mockCrossAppIntegration.initialize).toHaveBeenCalled();
50+
expect(mockCrossAppIntegration.processCrossAppAuthRequest).toHaveBeenCalled();
51+
});
52+
53+
it('should return false if silent login is not attempted', async () => {
54+
mockCrossAppIntegration.initialize.and.returnValue(Promise.resolve());
55+
mockCrossAppIntegration.processCrossAppAuthRequest.and.returnValue(Promise.resolve(false));
56+
57+
const result = await service.initializeCrossAppLogin();
58+
59+
expect(result).toBe(false);
60+
expect(mockCrossAppIntegration.initialize).toHaveBeenCalled();
61+
expect(mockCrossAppIntegration.processCrossAppAuthRequest).toHaveBeenCalled();
62+
});
63+
64+
it('should handle initialization timeout', async () => {
65+
mockCrossAppIntegration.initialize.and.returnValue(
66+
new Promise(() => {}) // Never resolves - simulates hanging
67+
);
68+
spyOn(console, 'warn');
69+
70+
const result = await service.initializeCrossAppLogin();
71+
72+
expect(result).toBe(false);
73+
expect(console.warn).toHaveBeenCalledWith('Cross-app auth initialization failed:', 'Cross-app auth initialization timed out');
74+
});
75+
76+
it('should handle initialization errors', async () => {
77+
const error = new Error('Network error');
78+
mockCrossAppIntegration.initialize.and.returnValue(Promise.reject(error));
79+
spyOn(console, 'warn');
80+
81+
const result = await service.initializeCrossAppLogin();
82+
83+
expect(result).toBe(false);
84+
expect(console.warn).toHaveBeenCalledWith('Cross-app auth initialization failed:', 'Network error');
85+
});
86+
87+
it('should handle various error types', async () => {
88+
const customError = new Error('Custom initialization error');
89+
mockCrossAppIntegration.initialize.and.returnValue(Promise.reject(customError));
90+
spyOn(console, 'warn');
91+
92+
const result = await service.initializeCrossAppLogin();
93+
94+
expect(result).toBe(false);
95+
expect(console.warn).toHaveBeenCalledWith('Cross-app auth initialization failed:', 'Custom initialization error');
96+
});
97+
98+
it('should handle silent login errors', async () => {
99+
mockCrossAppIntegration.initialize.and.returnValue(Promise.resolve());
100+
mockCrossAppIntegration.processCrossAppAuthRequest.and.returnValue(Promise.reject(new Error('Silent login failed')));
101+
spyOn(console, 'warn');
102+
103+
const result = await service.initializeCrossAppLogin();
104+
105+
expect(result).toBe(false);
106+
expect(console.warn).toHaveBeenCalledWith('Cross-app auth initialization failed:', 'Silent login failed');
107+
});
108+
});
109+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*!
2+
* @license
3+
* Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { Injectable, inject } from '@angular/core';
19+
import { CrossAppAuthIntegrationService } from './cross-app-auth-integration.service';
20+
21+
/**
22+
* Service responsible for initializing cross-application authentication during app startup.
23+
* Handles timeout management and error handling for the initialization process.
24+
*/
25+
@Injectable()
26+
export class CrossAppAuthInitializerService {
27+
private readonly crossAppIntegration = inject(CrossAppAuthIntegrationService);
28+
private readonly initializationTimeoutMs = 5000;
29+
30+
/**
31+
* Initializes the cross-application login process.
32+
*
33+
* This method attempts to initialize the cross-app authentication flow with a timeout,
34+
* and then performs a cross-app authentication request. If any error occurs during
35+
* initialization or authentication, it handles the error and returns `false`.
36+
*
37+
* @returns A promise that resolves to `true` if cross-app login is successful, or `false` if an error occurs.
38+
*/
39+
async initializeCrossAppLogin(): Promise<boolean> {
40+
try {
41+
await this.initializeWithTimeout();
42+
return this.crossAppIntegration.processCrossAppAuthRequest();
43+
} catch (error) {
44+
this.handleInitializationError(error);
45+
return false;
46+
}
47+
}
48+
49+
private async initializeWithTimeout(): Promise<void> {
50+
const initPromise = this.crossAppIntegration.initialize();
51+
const timeoutPromise = this.createTimeoutPromise();
52+
53+
await Promise.race([initPromise, timeoutPromise]);
54+
}
55+
56+
private createTimeoutPromise(): Promise<never> {
57+
return new Promise<never>((_, reject) =>
58+
setTimeout(() => reject(new Error('Cross-app auth initialization timed out')), this.initializationTimeoutMs)
59+
);
60+
}
61+
62+
private handleInitializationError(error: unknown): void {
63+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
64+
console.warn('Cross-app auth initialization failed:', errorMessage);
65+
}
66+
}

0 commit comments

Comments
 (0)