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
277 changes: 138 additions & 139 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions packages/react/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@corbado/react",
"version": "3.7.0-alpha.0",
"version": "3.7.0-alpha.1",
"description": "This package provides all the functionalities and UI components needed by developers when integration Corbado's Authentication management system in their react application",
"author": "Abdullah Shahbaz <abdullah_ghani@live.com>",
"homepage": "https://github.com/corbado/javascript#readme",
Expand Down Expand Up @@ -33,7 +33,7 @@
"dependencies": {
"@corbado/observe": "^0.0.1-next.4",
"@corbado/shared-util": "^1.0.12",
"@corbado/web-core": "^3.7.0-alpha.0",
"@corbado/web-core": "^3.7.0-alpha.2",
"i18next": "23.5.1",
"i18next-browser-languagedetector": "7.1.0",
"libphonenumber-js": "^1.10.59",
Expand Down
2 changes: 1 addition & 1 deletion packages/web-core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@corbado/web-core",
"version": "3.7.0-alpha.1",
"version": "3.7.0-alpha.2",
"description": "This package contains core functionalities which are exported by all the SDKs and UI packages of our JavaScript library",
"author": "Abdullah Shahbaz <abdullah_ghani@live.com>",
"homepage": "https://github.com/corbado/javascript#readme",
Expand Down
4 changes: 1 addition & 3 deletions packages/web-core/src/models/authProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,12 @@ export class AuthProcess {
export class TempAuthProcess {
readonly id: string;
readonly projectId: string;
readonly frontendApiUrl: string;
readonly expiresAt: number;

constructor(id: string, projectId: string, expiresAt: number, frontendApiUrl: string) {
constructor(id: string, projectId: string, expiresAt: number) {
this.id = id;
this.projectId = projectId;
this.expiresAt = expiresAt;
this.frontendApiUrl = frontendApiUrl;
}

isValid(): boolean {
Expand Down
8 changes: 1 addition & 7 deletions packages/web-core/src/models/emailVerifyFromUrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ type EmailVerifyFromUrlData = {
tempId: string;
projectId: string;
expires: number;
frontendApiUrl: string;
};
};

Expand Down Expand Up @@ -51,12 +50,7 @@ export class EmailVerifyFromUrl {
authType = AuthType.Signup;
}

const tempAuthProcess = new TempAuthProcess(
process.tempId,
process.projectId,
process.expires,
process.frontendApiUrl,
);
const tempAuthProcess = new TempAuthProcess(process.tempId, process.projectId, process.expires);

return new EmailVerifyFromUrl(data, token, tempAuthProcess, authType);
}
Expand Down
24 changes: 21 additions & 3 deletions packages/web-core/src/services/ProcessService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ import type { TempAuthProcess } from '../models/authProcess';
import { AuthProcess } from '../models/authProcess';
import { EmailVerifyFromUrl } from '../models/emailVerifyFromUrl';
import type { LastIdentifier } from '../models/lastIdentifier';
import { CorbadoError, PasskeyChallengeCancelledError, skipPasskeyAppendAfterHybridKey } from '../utils';
import {
CorbadoError,
isSafeRedirectUrl,
PasskeyChallengeCancelledError,
skipPasskeyAppendAfterHybridKey,
} from '../utils';
import { ClientStateService } from './ClientStateService';
import { WebAuthnService } from './WebAuthnService';

Expand Down Expand Up @@ -154,6 +159,15 @@ export class ProcessService {

try {
const emailVerifyFromUrl = EmailVerifyFromUrl.fromURL(encodedProcess, token);

if (emailVerifyFromUrl.process.projectId !== this.#projectId) {
return Err(CorbadoError.invalidConfig());
}

if (!emailVerifyFromUrl.process.isValid()) {
return Err(CorbadoError.invalidConfig());
}

this.#setApisV2ByTempProcess(emailVerifyFromUrl.process);

return Ok(emailVerifyFromUrl);
Expand Down Expand Up @@ -250,9 +264,9 @@ export class ProcessService {
}

#setApisV2ByTempProcess(tempProcess: TempAuthProcess): void {
const frontendApiUrl = tempProcess.frontendApiUrl;
const frontendApiUrl = this.#getDefaultFrontendApiUrl();
const config = new Configuration({
apiKey: tempProcess.projectId,
apiKey: this.#projectId,
basePath: frontendApiUrl,
});

Expand Down Expand Up @@ -524,6 +538,10 @@ export class ProcessService {
}

if (typed.socialData && typed.socialData.status === SocialDataStatusEnum.Started && typed.socialData.oauthUrl) {
if (!isSafeRedirectUrl(typed.socialData.oauthUrl)) {
return Err(CorbadoError.invalidConfig());
}

window.location.href = typed.socialData.oauthUrl;

return null;
Expand Down
1 change: 1 addition & 0 deletions packages/web-core/src/utils/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './getEmailLinkToken';
export * from './base64';
export * from './urlValidation';
52 changes: 52 additions & 0 deletions packages/web-core/src/utils/helpers/urlValidation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
const LOCALHOST_HOSTNAMES = ['localhost', '127.0.0.1', '[::1]'];

/**
* Returns true if the hostname is a local development host for which we allow the
* insecure http: scheme. Everything else must be https:.
*/
const isLocalhost = (hostname: string): boolean => LOCALHOST_HOSTNAMES.includes(hostname);

/**
* Validates a URL that will be used as a navigation target (e.g. an OAuth redirect
* assigned to window.location.href).
*
* Only https: is permitted, with an explicit http: exception for localhost during
* development.
*/
export const isSafeRedirectUrl = (rawUrl: string): boolean => {
let url: URL;
try {
url = new URL(rawUrl);
} catch {
return false;
}

if (url.protocol === 'https:') {
return true;
}

if (url.protocol === 'http:' && isLocalhost(url.hostname)) {
return true;
}

return false;
};

/**
* Validates that a frontend API base URL is an acceptable origin.
*
* Only https: origins are allowed, with an http: exception for localhost during
* development.
*/
export const isSafeFrontendApiUrl = (rawUrl: string): boolean => isSafeRedirectUrl(rawUrl);

/**
* Compares two URLs by origin. Returns false if either value is not a parseable URL.
*/
export const haveSameOrigin = (a: string, b: string): boolean => {
try {
return new URL(a).origin === new URL(b).origin;
} catch {
return false;
}
};
Loading