Skip to content

Commit

Permalink
fix: background pid refresh (#239)
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <[email protected]>
Co-authored-by: Berend Sliedrecht <[email protected]>
  • Loading branch information
TimoGlastra and berendsliedrecht authored Nov 26, 2024
1 parent 8c855ce commit a16b1ef
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 250 deletions.
83 changes: 40 additions & 43 deletions apps/easypid/src/app/(app)/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { type CredentialDataHandlerOptions, DeeplinkHandler, useHaptics } from '
import { HeroIcons, IconContainer } from '@package/ui'
import { useEffect, useState } from 'react'
import { useTheme } from 'tamagui'
import { WithBackgroundPidRefresh } from '../../features/pid/WithBackPidRefresh'

const jsonRecordIds = [activityStorage.recordId]

Expand Down Expand Up @@ -99,48 +98,46 @@ export default function AppLayout() {
return (
<AgentProvider agent={secureUnlock.context.agent}>
<WalletJsonStoreProvider agent={secureUnlock.context.agent} recordIds={jsonRecordIds}>
<WithBackgroundPidRefresh>
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen
options={{
presentation: 'modal',
}}
name="(home)/scan"
/>
<Stack.Screen
name="notifications/openIdPresentation"
options={{
gestureEnabled: false,
}}
/>
<Stack.Screen
name="notifications/openIdCredential"
options={{
gestureEnabled: false,
}}
/>
<Stack.Screen
name="notifications/offlinePresentation"
options={{
gestureEnabled: false,
}}
/>
<Stack.Screen name="credentials/index" options={headerNormalOptions} />
<Stack.Screen name="credentials/[id]/index" options={headerNormalOptions} />
<Stack.Screen name="credentials/[id]/attributes" options={headerNormalOptions} />
<Stack.Screen name="credentials/requestedAttributes" options={headerNormalOptions} />
<Stack.Screen name="menu/index" options={headerNormalOptions} />
<Stack.Screen name="menu/feedback" options={headerNormalOptions} />
<Stack.Screen name="menu/settings" options={headerNormalOptions} />
<Stack.Screen name="menu/about" options={headerNormalOptions} />
<Stack.Screen name="activity/index" options={headerNormalOptions} />
<Stack.Screen name="activity/[id]" options={headerNormalOptions} />
<Stack.Screen name="pinConfirmation" options={headerNormalOptions} />
<Stack.Screen name="pinLocked" options={headerNormalOptions} />
<Stack.Screen name="issuer" options={headerNormalOptions} />
<Stack.Screen name="pidSetup" />
</Stack>
</WithBackgroundPidRefresh>
<Stack screenOptions={{ headerShown: false }}>
<Stack.Screen
options={{
presentation: 'modal',
}}
name="(home)/scan"
/>
<Stack.Screen
name="notifications/openIdPresentation"
options={{
gestureEnabled: false,
}}
/>
<Stack.Screen
name="notifications/openIdCredential"
options={{
gestureEnabled: false,
}}
/>
<Stack.Screen
name="notifications/offlinePresentation"
options={{
gestureEnabled: false,
}}
/>
<Stack.Screen name="credentials/index" options={headerNormalOptions} />
<Stack.Screen name="credentials/[id]/index" options={headerNormalOptions} />
<Stack.Screen name="credentials/[id]/attributes" options={headerNormalOptions} />
<Stack.Screen name="credentials/requestedAttributes" options={headerNormalOptions} />
<Stack.Screen name="menu/index" options={headerNormalOptions} />
<Stack.Screen name="menu/feedback" options={headerNormalOptions} />
<Stack.Screen name="menu/settings" options={headerNormalOptions} />
<Stack.Screen name="menu/about" options={headerNormalOptions} />
<Stack.Screen name="activity/index" options={headerNormalOptions} />
<Stack.Screen name="activity/[id]" options={headerNormalOptions} />
<Stack.Screen name="pinConfirmation" options={headerNormalOptions} />
<Stack.Screen name="pinLocked" options={headerNormalOptions} />
<Stack.Screen name="issuer" options={headerNormalOptions} />
<Stack.Screen name="pidSetup" />
</Stack>
</WalletJsonStoreProvider>
</AgentProvider>
)
Expand Down
9 changes: 0 additions & 9 deletions apps/easypid/src/features/pid/WithBackPidRefresh.tsx

This file was deleted.

67 changes: 0 additions & 67 deletions apps/easypid/src/hooks/useBackgroundPidRefresh.ts

This file was deleted.

12 changes: 6 additions & 6 deletions apps/easypid/src/use-cases/ReceivePidUseCaseFlow.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { AusweisAuthFlow, type AusweisAuthFlowOptions, sendCommand } from '@animo-id/expo-ausweis-sdk'
import type { MdocRecord } from '@credo-ts/core'
import type { AppAgent } from '@easypid/agent'
import {
type OpenId4VciRequestTokenResponse,
type OpenId4VciResolvedCredentialOffer,
type OpenId4VciResolvedOauth2RedirectAuthorizationRequest,
type SdJwtVcRecord,
acquireAuthorizationCodeAccessToken,
import type {
OpenId4VciRequestTokenResponse,
OpenId4VciResolvedCredentialOffer,
OpenId4VciResolvedOauth2RedirectAuthorizationRequest,
SdJwtVcRecord,
} from '@package/agent'
import { acquireAuthorizationCodeAccessToken } from '@package/agent/src/invitation/handler'

export interface ReceivePidUseCaseFlowOptions
extends Pick<AusweisAuthFlowOptions, 'onAttachCard' | 'onStatusProgress' | 'onCardAttachedChanged'> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { ClaimFormat, MdocRecord, getJwkFromJson } from '@credo-ts/core'
import { SdJwtVcRecord } from '@credo-ts/core'
import type { AppAgent } from '@easypid/agent'
import type { OpenId4VciRequestTokenResponse, OpenId4VciResolvedCredentialOffer } from '@package/agent'
import {
type OpenId4VciRequestTokenResponse,
type OpenId4VciResolvedCredentialOffer,
SdJwtVcRecord,
acquireRefreshTokenAccessToken,
getRefreshCredentialMetadata,
receiveCredentialFromOpenId4VciOffer,
resolveOpenId4VciOffer,
setRefreshCredentialMetadata,
updateCredential,
} from '@package/agent'
} from '@package/agent/src/invitation/handler'
import { getBatchCredentialMetadata, setBatchCredentialMetadata } from '@package/agent/src/openid4vc/batchMetadata'
import {
getRefreshCredentialMetadata,
setRefreshCredentialMetadata,
} from '@package/agent/src/openid4vc/refreshMetadata'
import { updateCredential } from '@package/agent/src/storage/credential'
import { pidSchemes } from '../constants'
import { ReceivePidUseCaseFlow } from './ReceivePidUseCaseFlow'
import { C_PRIME_SD_JWT_MDOC_OFFER } from './bdrPidIssuerOffers'
Expand Down Expand Up @@ -55,9 +56,11 @@ export class RefreshPidUseCase {
public async retrieveCredentialsUsingExistingRecords({
sdJwt,
mdoc,
batchSize = 2,
}: {
sdJwt?: SdJwtVcRecord
mdoc?: MdocRecord
batchSize?: number
}) {
const existingRefreshMetadata =
(sdJwt ? getRefreshCredentialMetadata(sdJwt) : undefined) ??
Expand Down Expand Up @@ -94,7 +97,7 @@ export class RefreshPidUseCase {
resolvedCredentialOffer: this.resolvedCredentialOffer,
credentialConfigurationIdsToRequest,
clientId: RefreshPidUseCase.CLIENT_ID,
requestBatch: 2,
requestBatch: batchSize,
pidSchemes,
})

Expand Down
100 changes: 75 additions & 25 deletions packages/agent/src/batch.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
import { Mdoc, MdocRecord, SdJwtVcRecord, W3cCredentialRecord } from '@credo-ts/core'
import type { EitherAgent } from './agent'
import type { AppAgent } from '../../../apps/easypid/src/agent'
import { RefreshPidUseCase } from '../../../apps/easypid/src/use-cases/RefreshPidUseCase'
import type { EasyPIDAppAgent, EitherAgent } from './agent'
import { getCredentialCategoryMetadata } from './credentialCategoryMetadata'
import { decodeW3cCredential } from './format/credentialEncoding'
import { getBatchCredentialMetadata } from './openid4vc/batchMetadata'
import { getRefreshCredentialMetadata } from './openid4vc/refreshMetadata'
import { updateCredential } from './storage'

export async function refreshPid({
agent,
sdJwt,
mdoc,
batchSize,
}: { agent: AppAgent; sdJwt?: SdJwtVcRecord; mdoc?: MdocRecord; batchSize?: number }) {
console.log('refreshing PID')
const useCase = await RefreshPidUseCase.initialize({
agent,
})

await useCase.retrieveCredentialsUsingExistingRecords({
sdJwt,
mdoc,
batchSize,
})
}

/**
* If available, takes a credential from the batch.
*
Expand All @@ -14,31 +36,59 @@ export async function handleBatchCredential<CredentialRecord extends W3cCredenti
credentialRecord: CredentialRecord
): Promise<CredentialRecord> {
const batchMetadata = getBatchCredentialMetadata(credentialRecord)
if (!batchMetadata) return credentialRecord

// TODO: maybe we should also store the main credential in the additional credentials (and rename it)
// As right now the main one is mostly for display
const batchCredential = batchMetadata.additionalCredentials.pop()

// Store the record with the used credential removed. Even if the presentation fails we remove it, as we want to be careful
// if the presentation was shared
if (batchCredential) await updateCredential(agent, credentialRecord)

if (batchMetadata) {
const batchCredential = batchMetadata.additionalCredentials.pop()

if (batchCredential) {
// Store the record with the used credential removed. Even if the presentation fails we remove it, as we want to be careful
// if the presentation was shared
await updateCredential(agent, credentialRecord)

if (credentialRecord instanceof MdocRecord) {
return new MdocRecord({
mdoc: Mdoc.fromBase64Url(batchCredential as string),
}) as CredentialRecord
}
if (credentialRecord instanceof SdJwtVcRecord) {
return new SdJwtVcRecord({
compactSdJwtVc: batchCredential as string,
}) as CredentialRecord
}
if (credentialRecord instanceof W3cCredentialRecord) {
return new W3cCredentialRecord({
tags: { expandedTypes: [] },
credential: decodeW3cCredential(batchCredential),
}) as CredentialRecord
}
// Try to refresh the pid when we run out
// TODO: we should probably move this somewhere else at some point
const categoryMetadata = getCredentialCategoryMetadata(credentialRecord)
const refreshMetadata = getRefreshCredentialMetadata(credentialRecord)
if (
categoryMetadata?.credentialCategory === 'DE-PID' &&
refreshMetadata &&
batchMetadata.additionalCredentials.length === 0
) {
refreshPid({
agent: agent as EasyPIDAppAgent,
sdJwt: credentialRecord.type === 'SdJwtVcRecord' ? credentialRecord : undefined,
mdoc: credentialRecord.type === 'MdocRecord' ? credentialRecord : undefined,
// Get a batch of 5 for a single record type
batchSize: 5,
})
.catch((error) => {
// TODO: we should handle the case where the refresh token is expired
agent.config.logger.error('Error refreshing pid', {
error,
})
})
.then(() => {
agent.config.logger.debug('Successfully refreshed PID')
})
}

if (batchCredential) {
if (credentialRecord instanceof MdocRecord) {
return new MdocRecord({
mdoc: Mdoc.fromBase64Url(batchCredential as string),
}) as CredentialRecord
}
if (credentialRecord instanceof SdJwtVcRecord) {
return new SdJwtVcRecord({
compactSdJwtVc: batchCredential as string,
}) as CredentialRecord
}
if (credentialRecord instanceof W3cCredentialRecord) {
return new W3cCredentialRecord({
tags: { expandedTypes: [] },
credential: decodeW3cCredential(batchCredential),
}) as CredentialRecord
}
}

Expand Down
Loading

0 comments on commit a16b1ef

Please sign in to comment.