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
Original file line number Diff line number Diff line change
@@ -1,23 +1,73 @@
import {ApplicationToken, IdentityToken} from '../../session/schema.js'
import {ExchangeScopes} from '../../session/exchange.js'
import {IdentityToken} from '../../session/schema.js'
import {TokenRequestResult} from '../../session/exchange.js'
import {API} from '../../api.js'
import {Environment, serviceEnvironment} from '../../context/service.js'
import {BugError} from '../../../../public/node/error.js'
import {Result} from '../../../../public/node/result.js'

export abstract class IdentityClient {
abstract requestAccessToken(scopes: string[]): Promise<IdentityToken>

abstract exchangeAccessForApplicationTokens(
identityToken: IdentityToken,
scopes: ExchangeScopes,
store?: string,
): Promise<{[x: string]: ApplicationToken}>
abstract tokenRequest(params: {
[key: string]: string
}): Promise<Result<TokenRequestResult, {error: string; store?: string}>>

abstract refreshAccessToken(currentToken: IdentityToken): Promise<IdentityToken>

clientId(): string {
return ''
}
abstract clientId(): string

applicationId(_api: API): string {
return ''
applicationId(api: API): string {
switch (api) {
case 'admin': {
const environment = serviceEnvironment()
if (environment === Environment.Local) {
return 'e92482cebb9bfb9fb5a0199cc770fde3de6c8d16b798ee73e36c9d815e070e52'
} else if (environment === Environment.Production) {
return '7ee65a63608843c577db8b23c4d7316ea0a01bd2f7594f8a9c06ea668c1b775c'
} else {
return 'e92482cebb9bfb9fb5a0199cc770fde3de6c8d16b798ee73e36c9d815e070e52'
}
}
case 'partners': {
const environment = serviceEnvironment()
if (environment === Environment.Local) {
return 'df89d73339ac3c6c5f0a98d9ca93260763e384d51d6038da129889c308973978'
} else if (environment === Environment.Production) {
return '271e16d403dfa18082ffb3d197bd2b5f4479c3fc32736d69296829cbb28d41a6'
} else {
return 'df89d73339ac3c6c5f0a98d9ca93260763e384d51d6038da129889c308973978'
}
}
case 'storefront-renderer': {
const environment = serviceEnvironment()
if (environment === Environment.Local) {
return '46f603de-894f-488d-9471-5b721280ff49'
} else if (environment === Environment.Production) {
return 'ee139b3d-5861-4d45-b387-1bc3ada7811c'
} else {
return '46f603de-894f-488d-9471-5b721280ff49'
}
}
case 'business-platform': {
const environment = serviceEnvironment()
if (environment === Environment.Local) {
return 'ace6dc89-b526-456d-a942-4b8ef6acda4b'
} else if (environment === Environment.Production) {
return '32ff8ee5-82b8-4d93-9f8a-c6997cefb7dc'
} else {
return 'ace6dc89-b526-456d-a942-4b8ef6acda4b'
}
}
case 'app-management': {
const environment = serviceEnvironment()
if (environment === Environment.Production) {
return '7ee65a63608843c577db8b23c4d7316ea0a01bd2f7594f8a9c06ea668c1b775c'
} else {
return 'e92482cebb9bfb9fb5a0199cc770fde3de6c8d16b798ee73e36c9d815e070e52'
}
}
default:
throw new BugError(`Application id for API of type: ${api}`)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,122 @@
import {IdentityClient} from './identity-client.js'
import {ApplicationToken, IdentityToken} from '../../session/schema.js'
import {ExchangeScopes} from '../../session/exchange.js'
import {ExchangeScopes, TokenRequestResult} from '../../session/exchange.js'
import {ok, Result} from '../../../../public/node/result.js'
import {allDefaultScopes} from '../../session/scopes.js'
import {applicationId} from '../../session/identity.js'

export class IdentityMockClient extends IdentityClient {
private readonly mockUserId = '08978734-325e-44ce-bc65-34823a8d5180'
private readonly authTokenPrefix = 'mtkn_'

async requestAccessToken(_scopes: string[]): Promise<IdentityToken> {
return {} as IdentityToken
const tokens = this.generateTokens('identity')

return Promise.resolve({
accessToken: tokens.accessToken,
alias: '',
expiresAt: this.getFutureDate(1),
refreshToken: tokens.refreshToken,
scopes: allDefaultScopes(),
userId: this.mockUserId,
})
}

async exchangeAccessForApplicationTokens(
_identityToken: IdentityToken,
_scopes: ExchangeScopes,
_store?: string,
): Promise<{[x: string]: ApplicationToken}> {
return {}
return {
[applicationId('app-management')]: this.generateTokens(applicationId('app-management')),
[applicationId('business-platform')]: this.generateTokens(applicationId('business-platform')),
[applicationId('admin')]: this.generateTokens(applicationId('admin')),
[applicationId('partners')]: this.generateTokens(applicationId('partners')),
[applicationId('storefront-renderer')]: this.generateTokens(applicationId('storefront-renderer')),
}
}

async tokenRequest(params: {
[key: string]: string
}): Promise<Result<TokenRequestResult, {error: string; store?: string}>> {
const tokens = this.generateTokens(params?.audience ?? '')
return ok({
access_token: tokens.accessToken,
expires_in: this.getFutureDate(1).getTime(),
refresh_token: tokens.refreshToken,
scope: allDefaultScopes().join(' '),
})
}

async refreshAccessToken(_currentToken: IdentityToken): Promise<IdentityToken> {
return {} as IdentityToken
const tokens = this.generateTokens('identity')

return Promise.resolve({
accessToken: tokens.accessToken,
alias: '[email protected]',
expiresAt: this.getFutureDate(1),
refreshToken: tokens.refreshToken,
scopes: allDefaultScopes(),
userId: this.mockUserId,
})
}

clientId(): string {
return 'shopify-cli-development'
}

private readonly generateTokens = (appId: string) => {
const now = this.getCurrentUnixTimestamp()
const exp = now + 7200

const tokenPayload = {
act: {
iss: 'https://identity.shop.dev',
sub: this.clientId(),
},
aud: appId,
client_id: this.clientId(),
token_type: 'SLAT',
exp,
iat: now,
iss: 'https://identity.shop.dev',
scope: allDefaultScopes().join(' '),
sub: this.mockUserId,
sid: 'df63c65c-3731-48af-a28d-72ab16a6523a',
auth_time: now,
amr: ['pwd', 'device-auth'],
device_uuid: '8ba644c8-7d2f-4260-9311-86df09195ee8',
atl: 1.0,
}

const refreshTokenPayload = {
...tokenPayload,
token_use: 'refresh',
}

return {
accessToken: `${this.authTokenPrefix}${this.encodeTokenPayload(tokenPayload)}`,
refreshToken: `${this.authTokenPrefix}${this.encodeTokenPayload(refreshTokenPayload)}`,
expiresAt: new Date(exp * 1000),
scopes: allDefaultScopes(),
}
}

private getFutureDate(daysInFuture = 100): Date {
const futureDate = new Date()
futureDate.setDate(futureDate.getDate() + daysInFuture)
return futureDate
}

private getCurrentUnixTimestamp(): number {
return Math.floor(Date.now() / 1000)
}

private encodeTokenPayload(payload: object): string {
return Buffer.from(JSON.stringify(payload))
.toString('base64')
.replace(/[=]/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_')
}
}
Loading
Loading