Skip to content
Open
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
2 changes: 1 addition & 1 deletion packages/javascript/src/StorageManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class StorageManager<T> {

const dataToBeSaved: PartialData<T> = {...existingData};

delete dataToBeSaved[attribute as string];
Reflect.deleteProperty(dataToBeSaved, attribute as string);

const dataToBeSavedJSON: string = JSON.stringify(dataToBeSaved);

Expand Down
18 changes: 9 additions & 9 deletions packages/javascript/src/__legacy__/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ const DefaultConfig: Partial<AuthClientConfig<unknown>> = {
*/
export class AsgardeoAuthClient<T> {
private _storageManager!: StorageManager<T>;
private _config: () => Promise<AuthClientConfig>;
private _oidcProviderMetaData: () => Promise<OIDCDiscoveryApiResponse>;
private _authenticationHelper: AuthenticationHelper<T>;
private _cryptoUtils: Crypto;
private _cryptoHelper: IsomorphicCrypto;
private _config!: () => Promise<AuthClientConfig>;
private _oidcProviderMetaData!: () => Promise<OIDCDiscoveryApiResponse>;
private _authenticationHelper!: AuthenticationHelper<T>;
private _cryptoUtils!: Crypto;
private _cryptoHelper!: IsomorphicCrypto;

private static _instanceID: number;

Expand Down Expand Up @@ -241,7 +241,7 @@ export class AsgardeoAuthClient<T> {
await this._storageManager.setTemporaryDataParameter(pkceKey, codeVerifier, userId);
}

if (authRequestConfig['client_secret']) {
if (authRequestConfig['client_secret'] && configData.clientSecret) {
authRequestConfig['client_secret'] = configData.clientSecret;
}

Expand All @@ -250,10 +250,10 @@ export class AsgardeoAuthClient<T> {
redirectUri: configData.afterSignInUrl,
clientId: configData.clientId,
scopes: processOpenIDScopes(configData.scopes),
responseMode: configData.responseMode,
...(configData.responseMode && { responseMode: configData.responseMode }),
codeChallengeMethod: PKCEConstants.DEFAULT_CODE_CHALLENGE_METHOD,
codeChallenge,
prompt: configData.prompt,
...(codeChallenge && { codeChallenge }),
...(configData.prompt && { prompt: configData.prompt }),
},
{key: pkceKey},
authRequestConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ export class AuthenticationHelper<T> {
Object.keys(configData.endpoints).forEach((endpointName: string) => {
const snakeCasedName: string = endpointName.replace(/[A-Z]/g, (letter: string) => `_${letter.toLowerCase()}`);

oidcProviderMetaData[snakeCasedName] = configData?.endpoints ? configData.endpoints[endpointName] : '';
(oidcProviderMetaData as Record<string, string>)[snakeCasedName] = configData?.endpoints
? (configData.endpoints as Record<string, string>)[endpointName] ?? ''
: '';
});

return {...response, ...oidcProviderMetaData};
Expand Down Expand Up @@ -100,7 +102,9 @@ export class AuthenticationHelper<T> {
Object.keys(configData.endpoints).forEach((endpointName: string) => {
const snakeCasedName: string = endpointName.replace(/[A-Z]/g, (letter: string) => `_${letter.toLowerCase()}`);

oidcProviderMetaData[snakeCasedName] = configData?.endpoints ? configData.endpoints[endpointName] : '';
(oidcProviderMetaData as Record<string, string>)[snakeCasedName] = configData?.endpoints
? (configData.endpoints as Record<string, string>)[endpointName] ?? ''
: '';
});

return {...oidcProviderMetaData};
Expand All @@ -124,7 +128,9 @@ export class AuthenticationHelper<T> {
Object.keys(configData.endpoints).forEach((endpointName: string) => {
const snakeCasedName: string = endpointName.replace(/[A-Z]/g, (letter: string) => `_${letter.toLowerCase()}`);

oidcProviderMetaData[snakeCasedName] = configData?.endpoints ? configData.endpoints[endpointName] : '';
(oidcProviderMetaData as Record<string, string>)[snakeCasedName] = configData?.endpoints
? (configData.endpoints as Record<string, string>)[endpointName] ?? ''
: '';
});

const defaultEndpoints: OIDCDiscoveryApiResponse = {
Expand Down Expand Up @@ -231,7 +237,7 @@ export class AuthenticationHelper<T> {
.replace(TokenExchangeConstants.Placeholders.ACCESS_TOKEN, sessionData.access_token)
.replace(
TokenExchangeConstants.Placeholders.USERNAME,
this.getAuthenticatedUserInfo(sessionData.id_token).username,
this.getAuthenticatedUserInfo(sessionData.id_token).username || '',
)
.replace(TokenExchangeConstants.Placeholders.SCOPES, scope)
.replace(TokenExchangeConstants.Placeholders.CLIENT_ID, configData.clientId)
Expand Down
20 changes: 15 additions & 5 deletions packages/javascript/src/api/executeEmbeddedSignInFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,24 @@ const executeEmbeddedSignInFlow = async ({
);
}

const baseHeaders = {
'Content-Type': 'application/json',
Accept: 'application/json',
};

let finalHeaders: HeadersInit;
if (requestConfig.headers instanceof Headers) {
finalHeaders = requestConfig.headers;
} else if (requestConfig.headers && typeof requestConfig.headers === 'object') {
finalHeaders = { ...baseHeaders, ...(requestConfig.headers as Record<string, string>) };
} else {
finalHeaders = baseHeaders;
}

const response: Response = await fetch(url ?? `${baseUrl}/oauth2/authn`, {
...requestConfig,
method: requestConfig.method || 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
...requestConfig.headers,
},
headers: finalHeaders,
body: JSON.stringify(payload),
});

Expand Down
20 changes: 15 additions & 5 deletions packages/javascript/src/api/executeEmbeddedSignUpFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,24 @@ const executeEmbeddedSignUpFlow = async ({
);
}

const baseHeaders = {
'Content-Type': 'application/json',
Accept: 'application/json',
};

let finalHeaders: HeadersInit;
if (requestConfig.headers instanceof Headers) {
finalHeaders = requestConfig.headers;
} else if (requestConfig.headers && typeof requestConfig.headers === 'object') {
finalHeaders = { ...baseHeaders, ...(requestConfig.headers as Record<string, string>) };
} else {
finalHeaders = baseHeaders;
}

const response: Response = await fetch(url ?? `${baseUrl}/api/server/v1/flow/execute`, {
...requestConfig,
method: requestConfig.method || 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
...requestConfig.headers,
},
headers: finalHeaders,
body: JSON.stringify({
...(payload ?? {}),
flowType: EmbeddedFlowType.Registration,
Expand Down
13 changes: 12 additions & 1 deletion packages/javascript/src/api/getSchemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,19 @@ export interface GetSchemasConfig extends Omit<RequestInit, 'method'> {
* ```
*/
const getSchemas = async ({url, baseUrl, fetcher, ...requestConfig}: GetSchemasConfig): Promise<Schema[]> => {
const finalUrl = url ?? baseUrl;
if (!finalUrl) {
throw new AsgardeoAPIError(
'Either url or baseUrl must be provided',
'getSchemas-ValidationError-000',
'javascript',
400,
'Invalid Request',
);
}

try {
new URL(url ?? baseUrl);
new URL(finalUrl);
} catch (error) {
throw new AsgardeoAPIError(
`Invalid URL provided. ${error?.toString()}`,
Expand Down
13 changes: 12 additions & 1 deletion packages/javascript/src/api/getScim2Me.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,19 @@ export interface GetScim2MeConfig extends Omit<RequestInit, 'method'> {
* ```
*/
const getScim2Me = async ({url, baseUrl, fetcher, ...requestConfig}: GetScim2MeConfig): Promise<User> => {
const finalUrl = url ?? baseUrl;
if (!finalUrl) {
throw new AsgardeoAPIError(
'Either url or baseUrl must be provided',
'getScim2Me-ValidationError-000',
'javascript',
400,
'Invalid Request',
);
}

try {
new URL(url ?? baseUrl);
new URL(finalUrl);
} catch (error) {
throw new AsgardeoAPIError(
`Invalid URL provided. ${error?.toString()}`,
Expand Down
31 changes: 26 additions & 5 deletions packages/javascript/src/api/getUserInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ import AsgardeoAPIError from '../errors/AsgardeoAPIError';
* ```
*/
const getUserInfo = async ({url, ...requestConfig}: Partial<Request>): Promise<User> => {
if (!url) {
throw new AsgardeoAPIError(
'URL is required',
'getUserInfo-ValidationError-000',
'javascript',
400,
'Invalid Request',
);
}

try {
new URL(url);
} catch (error) {
Expand All @@ -49,14 +59,25 @@ const getUserInfo = async ({url, ...requestConfig}: Partial<Request>): Promise<U
);
}

// URL is guaranteed to be defined and valid due to validation above
const baseHeaders = {
'Content-Type': 'application/json',
Accept: 'application/json',
};

let finalHeaders: HeadersInit;
if (requestConfig.headers instanceof Headers) {
finalHeaders = requestConfig.headers;
} else if (requestConfig.headers && typeof requestConfig.headers === 'object') {
finalHeaders = { ...baseHeaders, ...(requestConfig.headers as Record<string, string>) };
} else {
finalHeaders = baseHeaders;
}

const response: Response = await fetch(url, {
...requestConfig,
method: 'GET',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
...requestConfig.headers,
},
headers: finalHeaders,
});

if (!response.ok) {
Expand Down
20 changes: 15 additions & 5 deletions packages/javascript/src/api/initializeEmbeddedSignInFlow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,24 @@ const initializeEmbeddedSignInFlow = async ({
}
});

const baseHeaders = {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
};

let finalHeaders: HeadersInit;
if (requestConfig.headers instanceof Headers) {
finalHeaders = requestConfig.headers;
} else if (requestConfig.headers && typeof requestConfig.headers === 'object') {
finalHeaders = { ...baseHeaders, ...(requestConfig.headers as Record<string, string>) };
} else {
finalHeaders = baseHeaders;
}

const response: Response = await fetch(url ?? `${baseUrl}/oauth2/authorize`, {
...requestConfig,
method: requestConfig.method || 'POST',
headers: {
...requestConfig.headers,
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
},
headers: finalHeaders,
body: searchParams.toString(),
});

Expand Down
35 changes: 31 additions & 4 deletions packages/javascript/src/api/updateMeProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,19 @@ const updateMeProfile = async ({
fetcher,
...requestConfig
}: UpdateMeProfileConfig): Promise<User> => {
const finalUrl = url ?? baseUrl;
if (!finalUrl) {
throw new AsgardeoAPIError(
'Either url or baseUrl must be provided',
'updateMeProfile-ValidationError-000',
'javascript',
400,
'Invalid Request',
);
}

try {
new URL(url ?? baseUrl);
new URL(finalUrl);
} catch (error) {
throw new AsgardeoAPIError(
`Invalid URL provided. ${error?.toString()}`,
Expand Down Expand Up @@ -146,12 +157,28 @@ const updateMeProfile = async ({
throw error;
}

// Type guard for error object with response/data properties
const isErrorWithResponse = (err: unknown): err is { response?: { data?: { detail?: string } } } => {
return typeof err === 'object' && err !== null;
};

const isErrorWithData = (err: unknown): err is { data?: { status?: number } } => {
return typeof err === 'object' && err !== null;
};

const errorMessage = isErrorWithResponse(error) && error.response?.data?.detail
? error.response.data.detail
: 'An error occurred while updating the user profile. Please try again.';

const errorStatus = isErrorWithData(error) && error.data?.status
? error.data.status
: 500;

throw new AsgardeoAPIError(
error?.response?.data?.detail ||
'An error occurred while updating the user profile. Please try again.',
errorMessage,
'updateMeProfile-NetworkError-001',
'javascript',
error?.data?.status,
errorStatus,
'Network Error',
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const getAuthorizeRequestUrlParams = (
authorizeRequestParams.set('response_type', 'code');
authorizeRequestParams.set('client_id', clientId as string);

authorizeRequestParams.set('scope', scopes);
authorizeRequestParams.set('scope', scopes ?? ScopeConstants.OPENID);
authorizeRequestParams.set('redirect_uri', redirectUri as string);

if (responseMode) {
Expand Down
4 changes: 2 additions & 2 deletions packages/javascript/src/utils/getRedirectBasedSignUpUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ import logger from './logger';
const getRedirectBasedSignUpUrl = (config: Config): string => {
const {baseUrl} = config;

if (!isRecognizedBaseUrlPattern(baseUrl)) return '';
if (!baseUrl || !isRecognizedBaseUrlPattern(baseUrl)) return '';

let signUpBaseUrl: string = baseUrl;

if (identifyPlatform(config) === Platform.Asgardeo) {
try {
const url: URL = new URL(baseUrl!);
const url: URL = new URL(baseUrl);

// Replace 'api.' with 'accounts.' in the hostname, preserving subdomains like 'dev.'
if (/([a-z0-9-]+\.)*api\.asgardeo\.io$/i.test(url.hostname)) {
Expand Down
3 changes: 2 additions & 1 deletion packages/javascript/src/utils/identifyPlatform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ const identifyPlatform = (config: Config): Platform => {

return Platform.Unknown;
} catch (error) {
logger.debug(`[identifyPlatform] Error identifying platform from base URL: ${baseUrl}. Error: ${error.message}`);
const errorMessage = error instanceof Error ? error.message : String(error);
logger.debug(`[identifyPlatform] Error identifying platform from base URL: ${baseUrl}. Error: ${errorMessage}`);

return Platform.Unknown;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/javascript/src/utils/processOpenIDScopes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import AsgardeoRuntimeError from '../errors/AsgardeoRuntimeError';
* processOpenIDScopes({}); // throws AsgardeoRuntimeError
* ```
*/
const processOpenIDScopes = (scopes: string | string[]): string => {
const processOpenIDScopes = (scopes?: string | string[]): string => {
let processedScopes: string[] = [];

if (scopes) {
Expand Down
Loading
Loading