Skip to content

Commit

Permalink
chore: update shield auth
Browse files Browse the repository at this point in the history
  • Loading branch information
jamalavedra committed Jun 20, 2024
1 parent 4a9f00b commit 170112d
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 36 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.7.9] - 2024-06-20
### Fix
- Fix Shield authentication

## [0.7.8] - 2024-06-11
### Added
- Add new linking/unlink methods
Expand Down
1 change: 1 addition & 0 deletions examples/apps/auth-sample/src/utils/openfortConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const openfort = new Openfort({
shieldConfiguration: {
shieldPublishableKey: process.env.NEXT_PUBLIC_SHIELD_API_KEY!,
shieldEncryptionKey: process.env.NEXT_PUBLIC_SHIELD_ENCRYPTION_SHARE!,
debug: true,
}
});

Expand Down
1 change: 1 addition & 0 deletions packages/platform-bridge/src/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ window.callFunction = async (jsonData: string) => {
request.transactionIntentId,
request.userOperationHash,
request.signature,
request.optimistic,
);

callbackToGame({
Expand Down
18 changes: 11 additions & 7 deletions sdk/src/authManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import {
import { BackendApiClients } from '@openfort/openapi-clients';
import * as crypto from 'crypto';
import {
Auth, InitAuthResponse, InitializeOAuthOptions, JWK, SIWEInitResponse,
Auth, InitAuthResponse, InitializeOAuthOptions, SIWEInitResponse,
AuthPlayerResponse, AuthResponse, OAuthProvider, ThirdPartyOAuthProvider, TokenType,
CodeChallengeMethodEnum,
} from './types';
import { SDKConfiguration } from './config';
import { OpenfortErrorType, withOpenfortError } from './errors/openfortError';
import { OpenfortError, OpenfortErrorType, withOpenfortError } from './errors/openfortError';
import InstanceManager from './instanceManager';
import { isBrowser } from './utils/helpers';
import DeviceCredentialsManager from './utils/deviceCredentialsManager';
Expand Down Expand Up @@ -293,11 +293,15 @@ export default class AuthManager {
}, OpenfortErrorType.USER_REGISTRATION_ERROR);
}

public async validateCredentials(
accessToken: string,
refreshToken: string,
jwk: JWK,
): Promise<Auth> {
public async validateCredentials(): Promise<Auth> {
const jwk = await this.instanceManager.getJWK();
const accessToken = this.instanceManager.getAccessToken()?.token;
const refreshToken = this.instanceManager.getRefreshToken();

if (!accessToken || !refreshToken || !jwk) {
throw new OpenfortError('Must be logged in to validate and refresh token', OpenfortErrorType.NOT_LOGGED_IN_ERROR);
}

try {
const key = (await importJWK(
{
Expand Down
8 changes: 6 additions & 2 deletions sdk/src/iframe/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,13 +338,17 @@ export class UpdateAuthenticationRequest implements IEventRequest {
}

export interface ShieldAuthentication {
auth: AuthType;
// Whether its using Openfort (either third-party or not) or a custom provider to verify the access token
auth: ShieldAuthType;
// The auth token, either idToken or accessToken
token: string;
// When using a third party auth provider, the provider name
authProvider?: string;
// When using a third party auth provider, the token type
tokenType?: string;
}

export enum AuthType {
export enum ShieldAuthType {
OPENFORT = 'openfort',
CUSTOM = 'custom',
}
Expand Down
3 changes: 2 additions & 1 deletion sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ export {
SessionResponse,
TransactionIntentResponse,
SDKOverrides,
AuthType,
} from './types';
export { ShieldAuthentication, AuthType } from './iframe/types';
export { ShieldAuthentication, ShieldAuthType } from './iframe/types';
export { Provider } from './evm/types';
export { SDKConfiguration, OpenfortConfiguration, ShieldConfiguration } from './config';
export { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer';
Expand Down
41 changes: 40 additions & 1 deletion sdk/src/instanceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
signerTypeStorageKey,
thirdPartyProviderStorageKey,
thirdPartyProviderTokenTypeStorageKey,
shieldAuthTypeStorageKey,
shieldAuthTokenStorageKey,
} from './storage/storage';

export type AccessToken = {
Expand All @@ -37,6 +39,8 @@ export default class InstanceManager {

private deviceID: string | null = null;

private shieldAuthType: string | null = null;

private chainId: string | null = null;

private accountAddress: string | null = null;
Expand Down Expand Up @@ -213,10 +217,11 @@ export default class InstanceManager {
const response = await this.backendApiClients.authenticationApi.getJwks(request);

if (response.data.keys.length === 0) {
throw new Error('No keys found');
throw new Error('Internal error: No JWKS keys found');
}

const jwtKey = response.data.keys[0];
this.setJWK(jwtKey);
this.jwk = {
kty: jwtKey.kty,
crv: jwtKey.crv,
Expand Down Expand Up @@ -318,4 +323,38 @@ export default class InstanceManager {
this.accountType = null;
this.persistentStorage.remove(accountTypeStorageKey);
}

public setShieldAuthType(accountType: string): void {
this.accountType = accountType;
this.persistentStorage.save(shieldAuthTypeStorageKey, accountType);
}

public getShieldAuthType(): string | null {
if (!this.accountType) {
this.accountType = this.persistentStorage.get(shieldAuthTypeStorageKey);
}
return this.accountType;
}

public removeShieldAuthType(): void {
this.accountType = null;
this.persistentStorage.remove(shieldAuthTypeStorageKey);
}

public setShieldAuthToken(accountType: string): void {
this.accountType = accountType;
this.persistentStorage.save(shieldAuthTokenStorageKey, accountType);
}

public getShieldAuthToken(): string | null {
if (!this.accountType) {
this.accountType = this.persistentStorage.get(shieldAuthTokenStorageKey);
}
return this.accountType;
}

public removeShieldAuthToken(): void {
this.accountType = null;
this.persistentStorage.remove(shieldAuthTokenStorageKey);
}
}
76 changes: 52 additions & 24 deletions sdk/src/openfort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
AuthResponse,
AuthPlayerResponse,
OpenfortEventMap,
AuthType,
} from './types';
import { SDKConfiguration } from './config';
import { EvmProvider } from './evm';
Expand All @@ -34,7 +35,7 @@ import { SessionStorage } from './storage/sessionStorage';
import IframeManager, {
IframeConfiguration,
} from './iframe/iframeManager';
import { ShieldAuthentication } from './iframe/types';
import { ShieldAuthType, ShieldAuthentication } from './iframe/types';

export class Openfort {
private signer?: ISigner;
Expand Down Expand Up @@ -98,6 +99,8 @@ export class Openfort {
this.instanceManager.removeChainID();
this.instanceManager.removeDeviceID();
this.instanceManager.removeJWK();
this.instanceManager.removeShieldAuthType();
this.instanceManager.removeShieldAuthToken();
}
}

Expand Down Expand Up @@ -174,8 +177,14 @@ export class Openfort {
shieldAuthentication?: ShieldAuthentication,
recoveryPassword?: string,
): Promise<void> {
const signer = this.newEmbeddedSigner(chainId, shieldAuthentication);
await this.validateAndRefreshToken();
if (shieldAuthentication) {
this.instanceManager.setShieldAuthType(shieldAuthentication?.auth);
if ((shieldAuthentication?.auth as ShieldAuthType) === ShieldAuthType.CUSTOM) {
this.instanceManager.setShieldAuthToken(shieldAuthentication?.token);
}
}
const signer = this.newEmbeddedSigner(chainId);
await signer.ensureEmbeddedAccount(recoveryPassword);
this.signer = signer;
this.instanceManager.setSignerType(SignerType.EMBEDDED);
Expand Down Expand Up @@ -308,6 +317,7 @@ export class Openfort {
public async initOAuth(
{ provider, options }: { provider: OAuthProvider; options?: InitializeOAuthOptions },
): Promise<InitAuthResponse> {
this.logout();
const authResponse = await this.authManager.initOAuth(provider, options);
return authResponse;
}
Expand Down Expand Up @@ -453,7 +463,7 @@ export class Openfort {
thirdPartyTokenType: null,
});
this.instanceManager.setRefreshToken(auth.refreshToken);
this.instanceManager.setPlayerID(auth.player);
if (auth.player) this.instanceManager.setPlayerID(auth.player);
}

/**
Expand Down Expand Up @@ -664,33 +674,34 @@ export class Openfort {
/**
* Validates and refreshes the access token if needed.
*/
public async validateAndRefreshToken() {
if (!this.credentialsProvided()) {
return;
}
const accessToken = this.instanceManager.getAccessToken();
const refreshToken = this.instanceManager.getRefreshToken();
const jwk = await this.instanceManager.getJWK();

if (!accessToken || !refreshToken || !jwk) {
return;
public async validateAndRefreshToken():Promise<void> {
const authType = this.credentialsProvided();
if (!authType) {
throw new OpenfortError('Must be logged in to validate and refresh token', OpenfortErrorType.NOT_LOGGED_IN_ERROR);
}

const auth = await this.authManager.validateCredentials(accessToken.token, refreshToken, jwk);
if (auth.accessToken !== accessToken.token) {
if (authType === AuthType.OPENFORT) {
const auth = await this.authManager.validateCredentials();
this.storeCredentials(auth);
}
if (this.signer && this.signer.useCredentials()) {
await this.signer.updateAuthentication();
if (this.signer && this.signer.useCredentials()) {
await this.signer.updateAuthentication();
}
}
}

private credentialsProvided() {
private credentialsProvided(): AuthType | null {
const token = this.instanceManager.getAccessToken();
const refreshToken = this.instanceManager.getRefreshToken();

return token && ((token.token && token.thirdPartyProvider && token.thirdPartyTokenType)
|| (token.token && refreshToken));
if (!token) {
return null;
}

if (token.token && token.thirdPartyProvider && token.thirdPartyTokenType) {
return AuthType.THIRD_PARTY;
} if (token.token && refreshToken) {
return AuthType.OPENFORT;
}
return null;
}

private async recoverSigner(): Promise<void> {
Expand Down Expand Up @@ -738,18 +749,35 @@ export class Openfort {

private newEmbeddedSigner(
chainId?: number,
shieldAuthentication?: ShieldAuthentication,
): EmbeddedSigner {
if (!this.credentialsProvided()) {
throw new OpenfortError('Must be logged in to configure embedded signer', OpenfortErrorType.NOT_LOGGED_IN_ERROR);
}

let shieldAuthType = this.instanceManager.getShieldAuthType();
if (!shieldAuthType) {
// TODO: remove, this is for backward compatibility
this.instanceManager.setShieldAuthType(ShieldAuthType.OPENFORT);
shieldAuthType = ShieldAuthType.OPENFORT;
// throw new OpenfortError('Shield auth type is not set', OpenfortErrorType.INVALID_CONFIGURATION);
}
const token = (shieldAuthType as ShieldAuthType) === ShieldAuthType.OPENFORT
? this.instanceManager.getAccessToken()?.token
: this.instanceManager.getShieldAuthToken();
if (!token) {
throw new OpenfortError('Shield auth token is not set', OpenfortErrorType.INVALID_CONFIGURATION);
}
const shieldAuth: ShieldAuthentication = {
auth: shieldAuthType as ShieldAuthType,
token,
};

const iframeConfiguration: IframeConfiguration = {
accessToken: this.instanceManager.getAccessToken()?.token ?? null,
thirdPartyProvider: this.instanceManager.getAccessToken()?.thirdPartyProvider ?? null,
thirdPartyTokenType: this.instanceManager.getAccessToken()?.thirdPartyTokenType ?? null,
chainId: !chainId ? Number(this.instanceManager.getChainID()) ?? null : chainId,
recovery: shieldAuthentication ?? null,
recovery: shieldAuth,
};
return new EmbeddedSigner(this.iframeManager, this.instanceManager, iframeConfiguration);
}
Expand Down
2 changes: 2 additions & 0 deletions sdk/src/storage/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export const chainIDStorageKey = 'openfort.chain_id';
export const jwksStorageKey = 'openfort.jwk';
export const deviceIDStorageKey = 'openfort.device_id';
export const accountTypeStorageKey = 'openfort.account_type';
export const shieldAuthTypeStorageKey = 'openfort.shield_auth_type';
export const shieldAuthTokenStorageKey = 'openfort.shield_auth_token';
export const accountAddressStorageKey = 'openfort.account_address';

export interface IStorage {
Expand Down
7 changes: 6 additions & 1 deletion sdk/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ export enum AccountType {
UPGRADEABLE_V5 = 'Upgradeable_v05',
}

export enum AuthType {
OPENFORT = 'openfort',
THIRD_PARTY = 'thirdParty',
}

export type Auth = {
player: string;
player?: string;
accessToken: string;
refreshToken: string;
};
Expand Down

0 comments on commit 170112d

Please sign in to comment.