From 0a2840565b2d60a9eee11170050afa5f0ef4d8b9 Mon Sep 17 00:00:00 2001 From: bobrimperator Date: Thu, 26 Dec 2024 19:39:36 +0100 Subject: [PATCH 1/8] refactor(typescript): base authenticator --- packages/ember-simple-auth/package.json | 3 +- .../src/authenticators/{base.js => base.ts} | 25 +++++++++--- pnpm-lock.yaml | 38 ++++++++++++++++++- 3 files changed, 59 insertions(+), 7 deletions(-) rename packages/ember-simple-auth/src/authenticators/{base.js => base.ts} (90%) diff --git a/packages/ember-simple-auth/package.json b/packages/ember-simple-auth/package.json index 55037b7b5..dd86b868b 100644 --- a/packages/ember-simple-auth/package.json +++ b/packages/ember-simple-auth/package.json @@ -62,7 +62,8 @@ "prettier": "3.3.3", "rollup": "4.25.0", "rollup-plugin-copy": "3.5.0", - "typescript": "^5.7.2" + "typescript": "^5.7.2", + "typescript-event-target": "^1.1.1" }, "publishConfig": { "registry": "https://registry.npmjs.org" diff --git a/packages/ember-simple-auth/src/authenticators/base.js b/packages/ember-simple-auth/src/authenticators/base.ts similarity index 90% rename from packages/ember-simple-auth/src/authenticators/base.js rename to packages/ember-simple-auth/src/authenticators/base.ts index 2f48412cd..6c40b0ba4 100644 --- a/packages/ember-simple-auth/src/authenticators/base.js +++ b/packages/ember-simple-auth/src/authenticators/base.ts @@ -1,6 +1,12 @@ import EmberObject from '@ember/object'; +import { TypedEventTarget, type TypedEventListener } from 'typescript-event-target'; -class AuthenticatorEventTarget extends EventTarget {} +export interface AuthenticatorEvents { + sessionDataUpdated: CustomEvent; + sessionDataInvalidated: CustomEvent; +} + +class AuthenticatorEventTarget extends TypedEventTarget {} /** The base class for all authenticators. __This serves as a starting point for @@ -167,15 +173,24 @@ export default class EsaBaseAuthenticator extends EmberObject { return Promise.resolve(); } - on(event, cb) { + on( + event: Event, + cb: TypedEventListener + ) { this.authenticatorEvents.addEventListener(event, cb); } - off(event, cb) { + off( + event: Event, + cb: TypedEventListener + ) { this.authenticatorEvents.removeEventListener(event, cb); } - trigger(event, value) { + trigger( + event: Event, + value: AuthenticatorEvents[Event] + ) { let customEvent; if (value) { customEvent = new CustomEvent(event, { detail: value }); @@ -183,6 +198,6 @@ export default class EsaBaseAuthenticator extends EmberObject { customEvent = new CustomEvent(event); } - this.authenticatorEvents.dispatchEvent(customEvent); + this.authenticatorEvents.dispatchTypedEvent(event, customEvent); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48b6daa65..f742fc738 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -322,6 +322,9 @@ importers: typescript: specifier: ^5.7.2 version: 5.7.2 + typescript-event-target: + specifier: ^1.1.1 + version: 1.1.1 packages/test-app: dependencies: @@ -9329,6 +9332,9 @@ packages: typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + typescript-event-target@1.1.1: + resolution: {integrity: sha512-dFSOFBKV6uwaloBCCUhxlD3Pr/P1a/tJdcmPrTXCHlEFD3faj0mztjcGn6VBAhQ0/Bdy8K3VWrrqwbt/ffsYsg==} + typescript-memoize@1.1.1: resolution: {integrity: sha512-GQ90TcKpIH4XxYTI2F98yEQYZgjNMOGPpOgdjIBhaLaWji5HPWlRnZ4AeA1hfBxtY7bCGDJsqDDHk/KaHOl5bA==} @@ -13725,6 +13731,26 @@ snapshots: dependencies: '@types/ms': 0.7.34 + '@types/ember@4.0.11': + dependencies: + '@types/ember__application': 4.0.11(@babel/core@7.26.0) + '@types/ember__array': 4.0.10(@babel/core@7.26.0) + '@types/ember__component': 4.0.22(@babel/core@7.26.0) + '@types/ember__controller': 4.0.12(@babel/core@7.26.0) + '@types/ember__debug': 4.0.8(@babel/core@7.26.0) + '@types/ember__engine': 4.0.11(@babel/core@7.26.0) + '@types/ember__error': 4.0.6 + '@types/ember__object': 4.0.12(@babel/core@7.26.0) + '@types/ember__polyfills': 4.0.6 + '@types/ember__routing': 4.0.22(@babel/core@7.26.0) + '@types/ember__runloop': 4.0.10 + '@types/ember__service': 4.0.9(@babel/core@7.26.0) + '@types/ember__string': 3.0.15 + '@types/ember__template': 4.0.7 + '@types/ember__test': 4.0.6(@babel/core@7.26.0) + '@types/ember__utils': 4.0.7 + '@types/rsvp': 4.0.9 + '@types/ember@4.0.11(@babel/core@7.26.0)': dependencies: '@types/ember__application': 4.0.11(@babel/core@7.26.0) @@ -13751,7 +13777,7 @@ snapshots: '@types/ember__application@4.0.11(@babel/core@7.26.0)': dependencies: '@glimmer/component': 1.1.2(@babel/core@7.26.0) - '@types/ember': 4.0.11(@babel/core@7.26.0) + '@types/ember': 4.0.11 '@types/ember__engine': 4.0.11(@babel/core@7.26.0) '@types/ember__object': 4.0.12(@babel/core@7.26.0) '@types/ember__owner': 4.0.9 @@ -13823,6 +13849,10 @@ snapshots: - '@babel/core' - supports-color + '@types/ember__runloop@4.0.10': + dependencies: + '@types/ember': 4.0.11 + '@types/ember__runloop@4.0.10(@babel/core@7.26.0)': dependencies: '@types/ember': 4.0.11(@babel/core@7.26.0) @@ -13848,6 +13878,10 @@ snapshots: - '@babel/core' - supports-color + '@types/ember__utils@4.0.7': + dependencies: + '@types/ember': 4.0.11 + '@types/ember__utils@4.0.7(@babel/core@7.26.0)': dependencies: '@types/ember': 4.0.11(@babel/core@7.26.0) @@ -22529,6 +22563,8 @@ snapshots: dependencies: is-typedarray: 1.0.0 + typescript-event-target@1.1.1: {} + typescript-memoize@1.1.1: {} typescript@3.9.10: {} From b8cb93e3fc7914badb3969579bf0200c3be75a98 Mon Sep 17 00:00:00 2001 From: bobrimperator Date: Thu, 26 Dec 2024 21:14:20 +0100 Subject: [PATCH 2/8] refactor(typescript): OAuth2PasswordGrantAuthenticator --- .../src/authenticators/base.ts | 8 +- ...word-grant.js => oauth2-password-grant.ts} | 155 ++++++++++++------ 2 files changed, 113 insertions(+), 50 deletions(-) rename packages/ember-simple-auth/src/authenticators/{oauth2-password-grant.js => oauth2-password-grant.ts} (78%) diff --git a/packages/ember-simple-auth/src/authenticators/base.ts b/packages/ember-simple-auth/src/authenticators/base.ts index 6c40b0ba4..d5db61022 100644 --- a/packages/ember-simple-auth/src/authenticators/base.ts +++ b/packages/ember-simple-auth/src/authenticators/base.ts @@ -115,7 +115,7 @@ export default class EsaBaseAuthenticator extends EmberObject { @member @public */ - restore() { + restore(...args: any[]): Promise { return Promise.reject(); } @@ -144,7 +144,7 @@ export default class EsaBaseAuthenticator extends EmberObject { @member @public */ - authenticate() { + authenticate(...args: any[]): Promise { return Promise.reject(); } @@ -169,7 +169,7 @@ export default class EsaBaseAuthenticator extends EmberObject { @member @public */ - invalidate() { + invalidate(...args: any[]): Promise { return Promise.resolve(); } @@ -189,7 +189,7 @@ export default class EsaBaseAuthenticator extends EmberObject { trigger( event: Event, - value: AuthenticatorEvents[Event] + value: AuthenticatorEvents[Event]['detail'] ) { let customEvent; if (value) { diff --git a/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.js b/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts similarity index 78% rename from packages/ember-simple-auth/src/authenticators/oauth2-password-grant.js rename to packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts index 24f045a0b..8212935c4 100644 --- a/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.js +++ b/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts @@ -1,5 +1,3 @@ -import { isEmpty } from '@ember/utils'; -import { run, later, cancel } from '@ember/runloop'; import { A, makeArray } from '@ember/array'; import { warn } from '@ember/debug'; import { getOwner } from '@ember/application'; @@ -7,6 +5,46 @@ import BaseAuthenticator from './base'; import isFastBoot from '../utils/is-fastboot'; import { waitFor } from '@ember/test-waiters'; import { isTesting } from '@embroider/macros'; +import type { Timer } from '@ember/runloop'; +import { run } from '@ember/runloop'; +import { cancel } from '@ember/runloop'; +import { later } from '@ember/runloop'; + +type OAuthResponseSuccess = { + access_token: string; + token_type: string; + expires_in?: number; + expires_at?: number; + refresh_token?: string; + scope?: string; +}; + +type OAuthPasswordRequestData = { + grant_type: string; + username: string; + password: string; + client_id?: string; + scope?: string; +}; + +type OAuthInvalidateRequestData = { + token_type_hint: 'access_token' | 'refresh_token'; + token: string; + client_id?: string; + scope?: string; +}; + +type OAuthRefreshRequestData = { + grant_type: 'refresh_token'; + refresh_token: string; + scope?: string; + client_id?: string; +}; + +type MakeRequestData = + | OAuthPasswordRequestData + | OAuthInvalidateRequestData + | OAuthRefreshRequestData; /** Authenticator that conforms to OAuth 2 @@ -46,7 +84,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator @default null @public */ - clientId = null; + clientId: string | null = null; /** The endpoint on the server that authentication and token refresh requests @@ -58,7 +96,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator @default '/token' @public */ - serverTokenEndpoint = '/token'; + serverTokenEndpoint: string = '/token'; /** The endpoint on the server that token revocation requests are sent to. Only @@ -75,7 +113,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator @default null @public */ - serverTokenRevocationEndpoint = null; + serverTokenRevocationEndpoint: string | null = null; /** Sets whether the authenticator automatically refreshes access tokens if the @@ -124,7 +162,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator return (Math.floor(Math.random() * (max - min)) + min) * 1000; } - _refreshTokenTimeout = null; + _refreshTokenTimeout: Timer | undefined = undefined; /** Restores the session from a session data object; __will return a resolving @@ -144,16 +182,17 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator @return {Promise} A promise that when it resolves results in the session becoming or remaining authenticated. If restoration fails, the promise will reject with the server response (in case the access token had expired and was refreshed using a refresh token); however, the authenticator reads that response already so if you need to read it again you need to clone the response object first @public */ - restore(data) { + restore(data: OAuthResponseSuccess) { return new Promise((resolve, reject) => { const now = new Date().getTime(); const refreshAccessTokens = this.get('refreshAccessTokens'); - if (!isEmpty(data['expires_at']) && data['expires_at'] < now) { + if (data['expires_at'] && data['expires_at'] < now) { if (refreshAccessTokens) { - this._refreshAccessToken(data['expires_in'], data['refresh_token'], data['scope']).then( - resolve, - reject - ); + this._refreshAccessToken( + data['expires_in'], + data['refresh_token'] as string, + data['scope'] + ).then(resolve, reject); } else { reject(); } @@ -228,13 +267,17 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator @return {Promise} A promise that when it resolves results in the session becoming authenticated. If authentication fails, the promise will reject with the server response; however, the authenticator reads that response already so if you need to read it again you need to clone the response object first @public */ - authenticate(identification, password, scope = [], headers = {}) { + authenticate(identification: string, password: string, scope = [], headers = {}) { return new Promise((resolve, reject) => { - const data = { grant_type: 'password', username: identification, password }; + const data: OAuthPasswordRequestData = { + grant_type: 'password', + username: identification, + password, + }; const serverTokenEndpoint = this.get('serverTokenEndpoint'); const scopesString = makeArray(scope).join(' '); - if (!isEmpty(scopesString)) { + if (scopesString.trim().length > 0) { data.scope = scopesString; } this.makeRequest(serverTokenEndpoint, data, headers).then( @@ -250,7 +293,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator expiresAt, response['refresh_token'] ); - if (!isEmpty(expiresAt)) { + if (expiresAt) { response = Object.assign(response, { expires_at: expiresAt }); } @@ -279,21 +322,21 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator @return {Promise} A promise that when it resolves results in the session being invalidated. If invalidation fails, the promise will reject with the server response (in case token revocation is used); however, the authenticator reads that response already so if you need to read it again you need to clone the response object first @public */ - invalidate(data) { + invalidate(data: OAuthResponseSuccess) { const serverTokenRevocationEndpoint = this.get('serverTokenRevocationEndpoint'); - function success(resolve) { + const success = (resolve: (value?: unknown) => void) => { cancel(this._refreshTokenTimeout); delete this._refreshTokenTimeout; resolve(); - } + }; return new Promise(resolve => { - if (isEmpty(serverTokenRevocationEndpoint)) { - success.apply(this, [resolve]); + if (!serverTokenRevocationEndpoint) { + success(resolve); } else { - const requests = []; - A(['access_token', 'refresh_token']).forEach(tokenType => { + const requests: Promise[] = []; + (['access_token', 'refresh_token'] as const).forEach(tokenType => { const token = data[tokenType]; - if (!isEmpty(token)) { + if (token) { requests.push( this.makeRequest(serverTokenRevocationEndpoint, { token_type_hint: tokenType, @@ -322,18 +365,29 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator @protected */ @waitFor - makeRequest(url, data, headers = {}) { + makeRequest( + url: string, + data: MakeRequestData, + headers: Record = {} + ): Promise { headers['Content-Type'] = 'application/x-www-form-urlencoded'; const clientId = this.get('clientId'); - if (!isEmpty(clientId)) { - data['client_id'] = this.get('clientId'); + if (clientId) { + data.client_id = clientId; } const body = Object.keys(data) .map(key => { - return `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`; + const value = data[key as keyof MakeRequestData]; + + if (value) { + return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`; + } else { + return null; + } }) + .filter(Boolean) .join('&'); const options = { @@ -349,13 +403,15 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator try { let json = JSON.parse(text); if (!response.ok) { - response.responseJSON = json; + // @TODO: migrate the old AJAX API. + (response as any).responseJSON = json; reject(response); } else { resolve(json); } } catch (SyntaxError) { - response.responseText = text; + // @TODO: migrate the old AJAX API. + (response as any).responseText = text; reject(response); } }); @@ -364,34 +420,41 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator }); } - _scheduleAccessTokenRefresh(expiresIn, expiresAt, refreshToken) { + _scheduleAccessTokenRefresh( + expiresIn: number | undefined, + expiresAt: number | null | undefined, + refreshToken: string | undefined + ) { const refreshAccessTokens = this.get('refreshAccessTokens') && !isFastBoot(getOwner(this)); if (refreshAccessTokens) { const now = new Date().getTime(); - if (isEmpty(expiresAt) && !isEmpty(expiresIn)) { + if (!expiresAt && expiresIn) { expiresAt = new Date(now + expiresIn * 1000).getTime(); } const offset = this.get('tokenRefreshOffset'); - if (!isEmpty(refreshToken) && !isEmpty(expiresAt) && expiresAt > now - offset) { + if (refreshToken && expiresAt && expiresAt > now - offset) { cancel(this._refreshTokenTimeout); delete this._refreshTokenTimeout; if (!isTesting()) { this._refreshTokenTimeout = later( - this, - this._refreshAccessToken, - expiresIn, - refreshToken, - expiresAt - now - offset + () => { + this._refreshAccessToken(expiresIn, refreshToken); + }, + (expiresAt as number) - now - offset ); } } } } - _refreshAccessToken(expiresIn, refreshToken, scope) { - const data = { grant_type: 'refresh_token', refresh_token: refreshToken }; + _refreshAccessToken(expiresIn: number | undefined, refreshToken: string, scope?: string) { + const data: OAuthRefreshRequestData = { + grant_type: 'refresh_token', + refresh_token: refreshToken, + scope: '', + }; const refreshAccessTokensWithScope = this.get('refreshAccessTokensWithScope'); - if (refreshAccessTokensWithScope && !isEmpty(scope)) { + if (refreshAccessTokensWithScope && scope) { data.scope = scope; } @@ -409,7 +472,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator expires_at: expiresAt, refresh_token: refreshToken, }); - if (refreshAccessTokensWithScope && !isEmpty(scope)) { + if (refreshAccessTokensWithScope && scope) { data.scope = scope; } this._scheduleAccessTokenRefresh(expiresIn, null, refreshToken); @@ -429,13 +492,13 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator }); } - _absolutizeExpirationTime(expiresIn) { - if (!isEmpty(expiresIn)) { + _absolutizeExpirationTime(expiresIn: number | undefined) { + if (expiresIn) { return new Date(new Date().getTime() + expiresIn * 1000).getTime(); } } - _validate(data) { - return !isEmpty(data['access_token']); + _validate(data: OAuthResponseSuccess) { + return Boolean(data['access_token']); } } From 26fbb36797eff37e45de3bdab4d78450792225b8 Mon Sep 17 00:00:00 2001 From: bobrimperator Date: Thu, 26 Dec 2024 22:27:23 +0100 Subject: [PATCH 3/8] feat(ember-simple-auth): note responseJSON and responseText as deprecated --- .../authenticators/oauth2-password-grant.ts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts b/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts index 8212935c4..a9c5cd634 100644 --- a/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts +++ b/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts @@ -1,4 +1,4 @@ -import { A, makeArray } from '@ember/array'; +import { makeArray } from '@ember/array'; import { warn } from '@ember/debug'; import { getOwner } from '@ember/application'; import BaseAuthenticator from './base'; @@ -6,9 +6,7 @@ import isFastBoot from '../utils/is-fastboot'; import { waitFor } from '@ember/test-waiters'; import { isTesting } from '@embroider/macros'; import type { Timer } from '@ember/runloop'; -import { run } from '@ember/runloop'; -import { cancel } from '@ember/runloop'; -import { later } from '@ember/runloop'; +import { run, later, cancel } from '@ember/runloop'; type OAuthResponseSuccess = { access_token: string; @@ -46,6 +44,17 @@ type MakeRequestData = | OAuthInvalidateRequestData | OAuthRefreshRequestData; +interface OAuth2Response extends Response { + /** + * @deprecated 'responseText' is deprecated. This is a legacy AJAX API. + */ + responseText: string; + /** + * @deprecated 'responseJSON' is deprecated. This is a legacy AJAX API. + */ + responseJSON: string; +} + /** Authenticator that conforms to OAuth 2 ([RFC 6749](http://tools.ietf.org/html/rfc6749)), specifically the _"Resource @@ -369,7 +378,7 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator url: string, data: MakeRequestData, headers: Record = {} - ): Promise { + ): Promise { headers['Content-Type'] = 'application/x-www-form-urlencoded'; const clientId = this.get('clientId'); @@ -403,15 +412,13 @@ export default class OAuth2PasswordGrantAuthenticator extends BaseAuthenticator try { let json = JSON.parse(text); if (!response.ok) { - // @TODO: migrate the old AJAX API. - (response as any).responseJSON = json; + (response as OAuth2Response).responseJSON = json; reject(response); } else { resolve(json); } } catch (SyntaxError) { - // @TODO: migrate the old AJAX API. - (response as any).responseText = text; + (response as OAuth2Response).responseText = text; reject(response); } }); From 93910de7460272973bb25889bc12fdfed7be7828 Mon Sep 17 00:00:00 2001 From: bobrimperator Date: Fri, 27 Dec 2024 01:47:36 +0100 Subject: [PATCH 4/8] feat(typescript): migrate OAuth2ImplicitGrant --- ...icit-grant.js => oauth2-implicit-grant.ts} | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) rename packages/ember-simple-auth/src/authenticators/{oauth2-implicit-grant.js => oauth2-implicit-grant.ts} (84%) diff --git a/packages/ember-simple-auth/src/authenticators/oauth2-implicit-grant.js b/packages/ember-simple-auth/src/authenticators/oauth2-implicit-grant.ts similarity index 84% rename from packages/ember-simple-auth/src/authenticators/oauth2-implicit-grant.js rename to packages/ember-simple-auth/src/authenticators/oauth2-implicit-grant.ts index 38ad550d8..07adac92f 100644 --- a/packages/ember-simple-auth/src/authenticators/oauth2-implicit-grant.js +++ b/packages/ember-simple-auth/src/authenticators/oauth2-implicit-grant.ts @@ -1,6 +1,5 @@ /** @module ember-simple-auth/authenticators/oauth2-implicit-grant **/ -import { isEmpty } from '@ember/utils'; import BaseAuthenticator from './base'; /** Parses the location hash (as received from `window.location.hash`) into an @@ -15,20 +14,33 @@ import BaseAuthenticator from './base'; @return {Object} An obect with individual properties and values for the data parsed from the location hash @memberof module:ember-simple-auth/authenticators/oauth2-implicit-grant */ -export function parseResponse(locationHash) { - let params = {}; +export function parseResponse(locationHash: string): Record { + let params: Record = {}; const query = locationHash.substring(locationHash.indexOf('?')); const regex = /([^#?&=]+)=([^&]*)/g; let match; // decode all parameter pairs while ((match = regex.exec(query)) !== null) { - params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]); + const [_, key, value] = match; + if (key && value) { + params[decodeURIComponent(key)] = decodeURIComponent(value); + } } return params; } +type ImplicitGrantData = { + response_type: string; + client_id: string; + redirect_uri: string; + scope: string; + state: string; + access_token: string; + error?: string; +}; + /** Authenticator that conforms to OAuth 2 ([RFC 6749](http://tools.ietf.org/html/rfc6749)), specifically the _"Implicit @@ -54,7 +66,7 @@ export default class OAuth2ImplicitGrantAuthenticator extends BaseAuthenticator @return {Promise} A promise that when it resolves results in the session becoming or remaining authenticated @public */ - restore(data) { + restore(data: ImplicitGrantData) { return new Promise((resolve, reject) => { if (!this._validateData(data)) { return reject('Could not restore session - "access_token" missing.'); @@ -79,7 +91,7 @@ export default class OAuth2ImplicitGrantAuthenticator extends BaseAuthenticator @return {Promise} A promise that when it resolves results in the session becoming authenticated @public */ - authenticate(hash) { + authenticate(hash: ImplicitGrantData) { return new Promise((resolve, reject) => { if (hash.error) { reject(hash.error); @@ -103,9 +115,8 @@ export default class OAuth2ImplicitGrantAuthenticator extends BaseAuthenticator return Promise.resolve(); } - _validateData(data) { + _validateData(data: ImplicitGrantData) { // see https://tools.ietf.org/html/rfc6749#section-4.2.2 - - return !isEmpty(data) && !isEmpty(data.access_token); + return data && data.access_token; } } From 23bc9093e3e04a8289a3e09c8b0ef3456b308821 Mon Sep 17 00:00:00 2001 From: bobrimperator Date: Fri, 27 Dec 2024 01:49:08 +0100 Subject: [PATCH 5/8] feat(typescript): export data types from authenticators --- .../src/authenticators/oauth2-implicit-grant.ts | 2 +- .../src/authenticators/oauth2-password-grant.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/ember-simple-auth/src/authenticators/oauth2-implicit-grant.ts b/packages/ember-simple-auth/src/authenticators/oauth2-implicit-grant.ts index 07adac92f..998b82f9c 100644 --- a/packages/ember-simple-auth/src/authenticators/oauth2-implicit-grant.ts +++ b/packages/ember-simple-auth/src/authenticators/oauth2-implicit-grant.ts @@ -31,7 +31,7 @@ export function parseResponse(locationHash: string): Record { return params; } -type ImplicitGrantData = { +export type ImplicitGrantData = { response_type: string; client_id: string; redirect_uri: string; diff --git a/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts b/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts index a9c5cd634..5edb787b9 100644 --- a/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts +++ b/packages/ember-simple-auth/src/authenticators/oauth2-password-grant.ts @@ -8,7 +8,7 @@ import { isTesting } from '@embroider/macros'; import type { Timer } from '@ember/runloop'; import { run, later, cancel } from '@ember/runloop'; -type OAuthResponseSuccess = { +export type OAuthResponseSuccess = { access_token: string; token_type: string; expires_in?: number; @@ -17,7 +17,7 @@ type OAuthResponseSuccess = { scope?: string; }; -type OAuthPasswordRequestData = { +export type OAuthPasswordRequestData = { grant_type: string; username: string; password: string; @@ -25,26 +25,26 @@ type OAuthPasswordRequestData = { scope?: string; }; -type OAuthInvalidateRequestData = { +export type OAuthInvalidateRequestData = { token_type_hint: 'access_token' | 'refresh_token'; token: string; client_id?: string; scope?: string; }; -type OAuthRefreshRequestData = { +export type OAuthRefreshRequestData = { grant_type: 'refresh_token'; refresh_token: string; scope?: string; client_id?: string; }; -type MakeRequestData = +export type MakeRequestData = | OAuthPasswordRequestData | OAuthInvalidateRequestData | OAuthRefreshRequestData; -interface OAuth2Response extends Response { +export interface OAuth2Response extends Response { /** * @deprecated 'responseText' is deprecated. This is a legacy AJAX API. */ From 3a607d19117deaded1fe764bc68b2bd8b3ae093b Mon Sep 17 00:00:00 2001 From: bobrimperator Date: Fri, 27 Dec 2024 01:51:23 +0100 Subject: [PATCH 6/8] feat(typescript): migrate TestAuthenticator --- .../ember-simple-auth/src/authenticators/{test.js => test.ts} | 4 ++-- packages/test-esa/tests/unit/authenticators/test-test.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename packages/ember-simple-auth/src/authenticators/{test.js => test.ts} (82%) diff --git a/packages/ember-simple-auth/src/authenticators/test.js b/packages/ember-simple-auth/src/authenticators/test.ts similarity index 82% rename from packages/ember-simple-auth/src/authenticators/test.js rename to packages/ember-simple-auth/src/authenticators/test.ts index 20e310992..6854603e5 100644 --- a/packages/ember-simple-auth/src/authenticators/test.js +++ b/packages/ember-simple-auth/src/authenticators/test.ts @@ -1,11 +1,11 @@ import BaseAuthenticator from './base'; export default class TestAuthenticator extends BaseAuthenticator { - restore(data) { + restore(data: any) { return Promise.resolve(data); } - authenticate(data) { + authenticate(data: any) { return Promise.resolve(data); } diff --git a/packages/test-esa/tests/unit/authenticators/test-test.js b/packages/test-esa/tests/unit/authenticators/test-test.js index 65a2e457e..634b0b3bd 100644 --- a/packages/test-esa/tests/unit/authenticators/test-test.js +++ b/packages/test-esa/tests/unit/authenticators/test-test.js @@ -5,7 +5,7 @@ module('TestAuthenticator', function (hooks) { let authenticator; hooks.beforeEach(function () { - authenticator = Test.create(); + authenticator = new Test(); }); module('#restore', function () { From 25dcee4c5da958b76f7b3cdc927d19dd3c1fc614 Mon Sep 17 00:00:00 2001 From: bobrimperator Date: Fri, 27 Dec 2024 02:10:24 +0100 Subject: [PATCH 7/8] feat(typescript): migrate DeviseAuthenticator --- .../src/authenticators/{devise.js => devise.ts} | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) rename packages/ember-simple-auth/src/authenticators/{devise.js => devise.ts} (94%) diff --git a/packages/ember-simple-auth/src/authenticators/devise.js b/packages/ember-simple-auth/src/authenticators/devise.ts similarity index 94% rename from packages/ember-simple-auth/src/authenticators/devise.js rename to packages/ember-simple-auth/src/authenticators/devise.ts index 86d0d2421..3980b85e1 100644 --- a/packages/ember-simple-auth/src/authenticators/devise.js +++ b/packages/ember-simple-auth/src/authenticators/devise.ts @@ -5,6 +5,8 @@ import { waitFor } from '@ember/test-waiters'; const JSON_CONTENT_TYPE = 'application/json'; +export type NestedRecord = Record>; + /** Authenticator that works with the Ruby gem [devise](https://github.com/plataformatec/devise). @@ -79,7 +81,7 @@ export default class DeviseAuthenticator extends BaseAuthenticator { @return {Promise} A promise that when it resolves results in the session becoming or remaining authenticated @public */ - restore(data) { + restore(data: Record) { return this._validate(data) ? Promise.resolve(data) : Promise.reject(); } @@ -102,14 +104,14 @@ export default class DeviseAuthenticator extends BaseAuthenticator { @return {Promise} A promise that when it resolves results in the session becoming authenticated. If authentication fails, the promise will reject with the server response; however, the authenticator reads that response already so if you need to read it again you need to clone the response object first @public */ - authenticate(identification, password) { + authenticate(identification: string, password: string) { return new Promise((resolve, reject) => { const { resourceName, identificationAttributeName, tokenAttributeName } = this.getProperties( 'resourceName', 'identificationAttributeName', 'tokenAttributeName' ); - const data = {}; + let data: NestedRecord = {}; data[resourceName] = { password }; data[resourceName][identificationAttributeName] = identification; @@ -161,7 +163,7 @@ export default class DeviseAuthenticator extends BaseAuthenticator { @protected */ @waitFor - makeRequest(data, options = {}) { + makeRequest(data: NestedRecord, options: { url?: string } = {}) { let url = options.url || this.get('serverTokenEndpoint'); let requestOptions = {}; let body = JSON.stringify(data); @@ -178,7 +180,7 @@ export default class DeviseAuthenticator extends BaseAuthenticator { return fetch(url, requestOptions); } - _validate(data) { + _validate(data: Record) { const tokenAttributeName = this.get('tokenAttributeName'); const identificationAttributeName = this.get('identificationAttributeName'); const resourceName = this.get('resourceName'); From b639ad2a52a100dea81b97a300d0b57ca305d213 Mon Sep 17 00:00:00 2001 From: bobrimperator Date: Fri, 27 Dec 2024 02:12:58 +0100 Subject: [PATCH 8/8] feat(ember-simple-auth): bump Torii deprecation to 8.0.0 --- packages/ember-simple-auth/src/authenticators/torii.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ember-simple-auth/src/authenticators/torii.js b/packages/ember-simple-auth/src/authenticators/torii.js index b36bef50d..334f75854 100644 --- a/packages/ember-simple-auth/src/authenticators/torii.js +++ b/packages/ember-simple-auth/src/authenticators/torii.js @@ -4,7 +4,7 @@ import BaseAuthenticator from './base'; deprecate('Ember Simple Auth: The Torii authenticator is deprecated.', false, { id: 'ember-simple-auth.authenticators.torii', - until: '7.0.0', + until: '8.0.0', for: 'ember-simple-auth', since: { enabled: '4.2.0',