• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

damienbod / angular-auth-oidc-client / 9308899843

30 May 2024 08:37PM CUT coverage: 92.928%. Remained the same
9308899843

Pull #1948

github

web-flow
Merge 9ccb2c964 into 33372edf1
Pull Request #1948: feat(core): adds angular 18 support

706 of 826 branches covered (85.47%)

Branch coverage included in aggregate %.

2566 of 2695 relevant lines covered (95.21%)

8.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.29
/projects/angular-auth-oidc-client/src/lib/auth-state/auth-state.service.ts
1
import { Injectable, inject } from '@angular/core';
2
import { BehaviorSubject, Observable, throwError } from 'rxjs';
3
import { distinctUntilChanged } from 'rxjs/operators';
4
import { OpenIdConfiguration } from '../config/openid-configuration';
5
import { AuthResult } from '../flows/callback-context';
6
import { LoggerService } from '../logging/logger.service';
7
import { EventTypes } from '../public-events/event-types';
8
import { PublicEventsService } from '../public-events/public-events.service';
9
import { StoragePersistenceService } from '../storage/storage-persistence.service';
10
import { TokenValidationService } from '../validation/token-validation.service';
11
import { AuthenticatedResult } from './auth-result';
12
import { AuthStateResult } from './auth-state';
13

14
const DEFAULT_AUTHRESULT = {
1✔
15
  isAuthenticated: false,
16
  allConfigsAuthenticated: [],
17
};
18

19
@Injectable({ providedIn: 'root' })
20
export class AuthStateService {
1✔
21
  private readonly storagePersistenceService = inject(
33✔
22
    StoragePersistenceService
23
  );
24

25
  private readonly loggerService = inject(LoggerService);
33✔
26

27
  private readonly publicEventsService = inject(PublicEventsService);
33✔
28

29
  private readonly tokenValidationService = inject(TokenValidationService);
33✔
30

31
  private readonly authenticatedInternal$ =
33✔
32
    new BehaviorSubject<AuthenticatedResult>(DEFAULT_AUTHRESULT);
33

34
  get authenticated$(): Observable<AuthenticatedResult> {
35
    return this.authenticatedInternal$
192✔
36
      .asObservable()
37
      .pipe(distinctUntilChanged());
38
  }
39

40
  setAuthenticatedAndFireEvent(allConfigs: OpenIdConfiguration[]): void {
41
    const result = this.composeAuthenticatedResult(allConfigs);
5✔
42

43
    this.authenticatedInternal$.next(result);
5✔
44
  }
45

46
  setUnauthenticatedAndFireEvent(
47
    currentConfig: OpenIdConfiguration,
48
    allConfigs: OpenIdConfiguration[]
49
  ): void {
50
    this.storagePersistenceService.resetAuthStateInStorage(currentConfig);
4✔
51

52
    const result = this.composeUnAuthenticatedResult(allConfigs);
4✔
53

54
    this.authenticatedInternal$.next(result);
4✔
55
  }
56

57
  updateAndPublishAuthState(authenticationResult: AuthStateResult): void {
58
    this.publicEventsService.fireEvent<AuthStateResult>(
1✔
59
      EventTypes.NewAuthenticationResult,
60
      authenticationResult
61
    );
62
  }
63

64
  setAuthorizationData(
65
    accessToken: string,
66
    authResult: AuthResult | null,
67
    currentConfig: OpenIdConfiguration,
68
    allConfigs: OpenIdConfiguration[]
69
  ): void {
70
    this.loggerService.logDebug(
3✔
71
      currentConfig,
72
      `storing the accessToken '${accessToken}'`
73
    );
74

75
    this.storagePersistenceService.write(
3✔
76
      'authzData',
77
      accessToken,
78
      currentConfig
79
    );
80
    this.persistAccessTokenExpirationTime(authResult, currentConfig);
3✔
81
    this.setAuthenticatedAndFireEvent(allConfigs);
3✔
82
  }
83

84
  getAccessToken(configuration: OpenIdConfiguration | null): string {
85
    if (!configuration) {
3!
86
      return '';
×
87
    }
88

89
    if (!this.isAuthenticated(configuration)) {
3✔
90
      return '';
1✔
91
    }
92

93
    const token = this.storagePersistenceService.getAccessToken(configuration);
2✔
94

95
    return this.decodeURIComponentSafely(token);
2✔
96
  }
97

98
  getIdToken(configuration: OpenIdConfiguration | null): string {
99
    if (!configuration) {
2!
100
      return '';
×
101
    }
102

103
    if (!this.isAuthenticated(configuration)) {
2✔
104
      return '';
1✔
105
    }
106

107
    const token = this.storagePersistenceService.getIdToken(configuration);
1✔
108

109
    return this.decodeURIComponentSafely(token);
1✔
110
  }
111

112
  getRefreshToken(configuration: OpenIdConfiguration | null): string {
113
    if (!configuration) {
2!
114
      return '';
×
115
    }
116

117
    if (!this.isAuthenticated(configuration)) {
2✔
118
      return '';
1✔
119
    }
120

121
    const token = this.storagePersistenceService.getRefreshToken(configuration);
1✔
122

123
    return this.decodeURIComponentSafely(token);
1✔
124
  }
125

126
  getAuthenticationResult(
127
    configuration: OpenIdConfiguration | null
128
  ): AuthResult | null {
129
    if (!configuration) {
3!
130
      return null;
×
131
    }
132

133
    if (!this.isAuthenticated(configuration)) {
3✔
134
      return null;
1✔
135
    }
136

137
    return this.storagePersistenceService.getAuthenticationResult(
2✔
138
      configuration
139
    );
140
  }
141

142
  areAuthStorageTokensValid(
143
    configuration: OpenIdConfiguration | null
144
  ): boolean {
145
    if (!configuration) {
5!
146
      return false;
×
147
    }
148

149
    if (!this.isAuthenticated(configuration)) {
5✔
150
      return false;
1✔
151
    }
152

153
    if (this.hasIdTokenExpiredAndRenewCheckIsEnabled(configuration)) {
4✔
154
      this.loggerService.logDebug(
1✔
155
        configuration,
156
        'persisted idToken is expired'
157
      );
158

159
      return false;
1✔
160
    }
161

162
    if (this.hasAccessTokenExpiredIfExpiryExists(configuration)) {
3✔
163
      this.loggerService.logDebug(
1✔
164
        configuration,
165
        'persisted accessToken is expired'
166
      );
167

168
      return false;
1✔
169
    }
170

171
    this.loggerService.logDebug(
2✔
172
      configuration,
173
      'persisted idToken and accessToken are valid'
174
    );
175

176
    return true;
2✔
177
  }
178

179
  hasIdTokenExpiredAndRenewCheckIsEnabled(
180
    configuration: OpenIdConfiguration
181
  ): boolean {
182
    const {
183
      renewTimeBeforeTokenExpiresInSeconds,
184
      triggerRefreshWhenIdTokenExpired,
185
      disableIdTokenValidation,
186
    } = configuration;
3✔
187

188
    if (!triggerRefreshWhenIdTokenExpired || disableIdTokenValidation) {
3✔
189
      return false;
1✔
190
    }
191
    const tokenToCheck =
192
      this.storagePersistenceService.getIdToken(configuration);
2✔
193

194
    const idTokenExpired = this.tokenValidationService.hasIdTokenExpired(
2✔
195
      tokenToCheck,
196
      configuration,
197
      renewTimeBeforeTokenExpiresInSeconds
198
    );
199

200
    if (idTokenExpired) {
2✔
201
      this.publicEventsService.fireEvent<boolean>(
2✔
202
        EventTypes.IdTokenExpired,
203
        idTokenExpired
204
      );
205
    }
206

207
    return idTokenExpired;
2✔
208
  }
209

210
  hasAccessTokenExpiredIfExpiryExists(
211
    configuration: OpenIdConfiguration
212
  ): boolean {
213
    const { renewTimeBeforeTokenExpiresInSeconds } = configuration;
2✔
214
    const accessTokenExpiresIn = this.storagePersistenceService.read(
2✔
215
      'access_token_expires_at',
216
      configuration
217
    );
218
    const accessTokenHasNotExpired =
219
      this.tokenValidationService.validateAccessTokenNotExpired(
2✔
220
        accessTokenExpiresIn,
221
        configuration,
222
        renewTimeBeforeTokenExpiresInSeconds
223
      );
224

225
    const hasExpired = !accessTokenHasNotExpired;
2✔
226

227
    if (hasExpired) {
2✔
228
      this.publicEventsService.fireEvent<boolean>(
1✔
229
        EventTypes.TokenExpired,
230
        hasExpired
231
      );
232
    }
233

234
    return hasExpired;
2✔
235
  }
236

237
  isAuthenticated(configuration: OpenIdConfiguration | null): boolean {
238
    if (!configuration) {
21!
239
      throwError(
×
240
        () =>
241
          new Error(
×
242
            'Please provide a configuration before setting up the module'
243
          )
244
      );
245

246
      return false;
×
247
    }
248

249
    const hasAccessToken =
250
      !!this.storagePersistenceService.getAccessToken(configuration);
21✔
251
    const hasIdToken =
252
      !!this.storagePersistenceService.getIdToken(configuration);
21✔
253

254
    return hasAccessToken && hasIdToken;
21✔
255
  }
256

257
  private decodeURIComponentSafely(token: string): string {
258
    if (token) {
4✔
259
      return decodeURIComponent(token);
3✔
260
    } else {
261
      return '';
1✔
262
    }
263
  }
264

265
  private persistAccessTokenExpirationTime(
266
    authResult: AuthResult | null,
267
    configuration: OpenIdConfiguration
268
  ): void {
269
    if (authResult?.expires_in) {
3✔
270
      const accessTokenExpiryTime =
271
        new Date(new Date().toUTCString()).valueOf() +
2✔
272
        authResult.expires_in * 1000;
273

274
      this.storagePersistenceService.write(
2✔
275
        'access_token_expires_at',
276
        accessTokenExpiryTime,
277
        configuration
278
      );
279
    }
280
  }
281

282
  private composeAuthenticatedResult(
283
    allConfigs: OpenIdConfiguration[]
284
  ): AuthenticatedResult {
285
    if (allConfigs.length === 1) {
5✔
286
      const { configId } = allConfigs[0];
3✔
287

288
      return {
3✔
289
        isAuthenticated: true,
290
        allConfigsAuthenticated: [
291
          { configId: configId ?? '', isAuthenticated: true },
3!
292
        ],
293
      };
294
    }
295

296
    return this.checkAllConfigsIfTheyAreAuthenticated(allConfigs);
2✔
297
  }
298

299
  private composeUnAuthenticatedResult(
300
    allConfigs: OpenIdConfiguration[]
301
  ): AuthenticatedResult {
302
    if (allConfigs.length === 1) {
4✔
303
      const { configId } = allConfigs[0];
2✔
304

305
      return {
2✔
306
        isAuthenticated: false,
307
        allConfigsAuthenticated: [
308
          { configId: configId ?? '', isAuthenticated: false },
2!
309
        ],
310
      };
311
    }
312

313
    return this.checkAllConfigsIfTheyAreAuthenticated(allConfigs);
2✔
314
  }
315

316
  private checkAllConfigsIfTheyAreAuthenticated(
317
    allConfigs: OpenIdConfiguration[]
318
  ): AuthenticatedResult {
319
    const allConfigsAuthenticated = allConfigs.map((config) => ({
8✔
320
      configId: config.configId ?? '',
8!
321
      isAuthenticated: this.isAuthenticated(config),
322
    }));
323

324
    const isAuthenticated = allConfigsAuthenticated.every(
4✔
325
      (x) => !!x.isAuthenticated
6✔
326
    );
327

328
    return { allConfigsAuthenticated, isAuthenticated };
4✔
329
  }
330
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc