Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/ember-simple-auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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<any>;
sessionDataInvalidated: CustomEvent;
}

class AuthenticatorEventTarget extends TypedEventTarget<AuthenticatorEvents> {}

/**
The base class for all authenticators. __This serves as a starting point for
Expand Down Expand Up @@ -109,7 +115,7 @@ export default class EsaBaseAuthenticator extends EmberObject {
@member
@public
*/
restore() {
restore(...args: any[]): Promise<unknown> {
return Promise.reject();
}

Expand Down Expand Up @@ -138,7 +144,7 @@ export default class EsaBaseAuthenticator extends EmberObject {
@member
@public
*/
authenticate() {
authenticate(...args: any[]): Promise<unknown> {
return Promise.reject();
}

Expand All @@ -163,26 +169,35 @@ export default class EsaBaseAuthenticator extends EmberObject {
@member
@public
*/
invalidate() {
invalidate(...args: any[]): Promise<unknown> {
return Promise.resolve();
}

on(event, cb) {
on<Event extends keyof AuthenticatorEvents>(
event: Event,
cb: TypedEventListener<AuthenticatorEvents, Event>
) {
this.authenticatorEvents.addEventListener(event, cb);
}

off(event, cb) {
off<Event extends keyof AuthenticatorEvents>(
event: Event,
cb: TypedEventListener<AuthenticatorEvents, Event>
) {
this.authenticatorEvents.removeEventListener(event, cb);
}

trigger(event, value) {
trigger<Event extends keyof AuthenticatorEvents>(
event: Event,
value: AuthenticatorEvents[Event]['detail']
) {
let customEvent;
if (value) {
customEvent = new CustomEvent(event, { detail: value });
} else {
customEvent = new CustomEvent(event);
}

this.authenticatorEvents.dispatchEvent(customEvent);
this.authenticatorEvents.dispatchTypedEvent(event, customEvent);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { waitFor } from '@ember/test-waiters';

const JSON_CONTENT_TYPE = 'application/json';

export type NestedRecord = Record<string, string | Record<string, string>>;

/**
Authenticator that works with the Ruby gem
[devise](https://github.com/plataformatec/devise).
Expand Down Expand Up @@ -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<string, NestedRecord>) {
return this._validate(data) ? Promise.resolve(data) : Promise.reject();
}

Expand All @@ -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;

Expand Down Expand Up @@ -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);
Expand All @@ -178,7 +180,7 @@ export default class DeviseAuthenticator extends BaseAuthenticator {
return fetch(url, requestOptions);
}

_validate(data) {
_validate(data: Record<string, NestedRecord>) {
const tokenAttributeName = this.get('tokenAttributeName');
const identificationAttributeName = this.get('identificationAttributeName');
const resourceName = this.get('resourceName');
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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<string, string> {
let params: Record<string, string> = {};
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;
}

export 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
Expand All @@ -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.');
Expand All @@ -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);
Expand All @@ -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;
}
}
Loading
Loading