-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(IT Wallet): [SIW-1299] Credentials status attestation at app st…
…artup (#6138) > [!WARNING] > This PR depends on #6128 ## Short description This PR introduces status attestations for credentials in the wallet. At app startup, **if the wallet is valid**, a saga checks each credential with the following logic: - It fetches the status attestation if the credential does not have one or there was an unexpected error during the previous attempt; - It fetches the status attestation if the previous one is expired (the current date is compared to the `exp` field ); - It does not fetch the status attestation if the previous one is not expired yet; - It does not fetch the status attestation if the credential is invalid/revoked. ## List of changes proposed in this pull request - Added `storedStatusAttestation` key to `StoredCredential` - Added `checkCredentialsStatusAttestation` saga, called after `checkWalletInstanceStateSaga` - Created `getCredentialStatus` function to get the overall status of a credential, using the status attestation first and then the expiration date - Modified `DateClaimItem` component to receive the credential status from outside and display the badge "Valid", "Not valid", "Expiring" accordingly - Passed the credential's status to its wallet card representation to be consistent ## How to test There are different cases to test. #### Credential without status attestation 1. Get a new credential and restart the app 2. Check the network for the status attestation HTTP call 3. Check the Redux store, the credential should contain `storedStatusAttestation` with a valid attestation #### Credential with valid status attestation 1. Restart the app with a credential that has a valid status attestation 2. Check the network, no status attestation HTTP call should appear 3. Check the Redux store, the credential should contain `storedStatusAttestation` with a valid attestation #### Credential with expired status attestation 1. Restart the app with a credential that has an expired status attestation (one way to fake the expiration is to modify the `new Date()` in `shouldRequestStatusAttestation` with a date after `exp`) 2. Check the network for the status attestation HTTP call 3. Check the Redux store, the credential should contain `storedStatusAttestation` with the new attestation #### Revoked credential 1. Restart the app with a credential that has an expired status attestation (fake an expired status attestation to force a refresh) 2. Check the network for the status attestation HTTP call (use an HTTP proxy to force a 404 response) 3. Check the Redux store, the credential should contain `storedStatusAttestation` with `credentialStatus: invalid` 4. Check the cards in the wallet to see the invalid credential 5. Check the credential detail to see the "Not valid" badge 7. Restart the app, no further status attestation HTTP calls should appear because the credential is invalid --------- Co-authored-by: LazyAfternoons <[email protected]>
- Loading branch information
1 parent
913aa75
commit 716eb9b
Showing
20 changed files
with
499 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"iat": 1724913816, | ||
"exp": 1725000216, | ||
"cnf": { | ||
"jwk": { | ||
"x": "ASVPcUncSZ_N1dxen8AbV1Q_hID3ZFA8n1wL27YO8D8B", | ||
"y": "TgIEMB8-1SqHMFheJWcI0oFmslC32Ef1zGwCKv4lM1c", | ||
"kty": "EC", | ||
"kid": "RAeSKpaAt8kagQMnScq6lM53aaRG7jIzQIhFdtMRW40", | ||
"crv": "P-256" | ||
} | ||
}, | ||
"credential_hash": "07157f9a54a5d50929daab85a79dc622ffc991c3b52099ba7058f672c651497a", | ||
"credential_hash_alg": "S256" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
ts/features/itwallet/common/utils/__tests__/itwCredentialStatusAttestationUtils.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import { shouldRequestStatusAttestation } from "../itwCredentialStatusAttestationUtils"; | ||
import { CredentialType, ItwStatusAttestationMocks } from "../itwMocksUtils"; | ||
import { StoredCredential } from "../itwTypesUtils"; | ||
|
||
describe("isStatusAttestationMissingOrExpired", () => { | ||
const baseMockCredential: StoredCredential = { | ||
credential: "", | ||
credentialType: CredentialType.DRIVING_LICENSE, | ||
format: "vc+sd-jwt", | ||
keyTag: "9020c6f8-01be-4236-9b6f-834af9dcbc63", | ||
issuerConf: {} as StoredCredential["issuerConf"], | ||
parsedCredential: {} | ||
}; | ||
|
||
it("return true when the status attestation is missing", () => { | ||
expect(shouldRequestStatusAttestation(baseMockCredential)).toEqual(true); | ||
}); | ||
|
||
it("return true when the parsed status attestation is null", () => { | ||
const mockCredential: StoredCredential = { | ||
...baseMockCredential, | ||
storedStatusAttestation: { | ||
credentialStatus: "unknown" | ||
} | ||
}; | ||
expect(shouldRequestStatusAttestation(mockCredential)).toEqual(true); | ||
}); | ||
|
||
it("return true when the status attestation is expired", () => { | ||
jest.useFakeTimers().setSystemTime(new Date("2024-08-27T10:30:00+00:00")); | ||
|
||
const mockCredential: StoredCredential = { | ||
...baseMockCredential, | ||
storedStatusAttestation: { | ||
credentialStatus: "valid", | ||
statusAttestation: "abc", | ||
parsedStatusAttestation: { | ||
...ItwStatusAttestationMocks.mdl, | ||
exp: 1724752800 // 2024-08-27T10:00:00+00:00 | ||
} | ||
} | ||
}; | ||
expect(shouldRequestStatusAttestation(mockCredential)).toEqual(true); | ||
}); | ||
|
||
it("return false when the status attestation is still valid", () => { | ||
jest.useFakeTimers().setSystemTime(new Date("2024-08-27T10:30:00+00:00")); | ||
|
||
const mockCredential: StoredCredential = { | ||
...baseMockCredential, | ||
storedStatusAttestation: { | ||
credentialStatus: "valid", | ||
statusAttestation: "abc", | ||
parsedStatusAttestation: { | ||
...ItwStatusAttestationMocks.mdl, | ||
exp: 1724781600 // 2024-08-27T18:00:00+00:00, | ||
} | ||
} | ||
}; | ||
expect(shouldRequestStatusAttestation(mockCredential)).toEqual(false); | ||
}); | ||
|
||
it("return false when the credential status is invalid", () => { | ||
jest.useFakeTimers().setSystemTime(new Date("2024-08-27T10:30:00+00:00")); | ||
|
||
const mockCredential: StoredCredential = { | ||
...baseMockCredential, | ||
storedStatusAttestation: { | ||
credentialStatus: "invalid" | ||
} | ||
}; | ||
expect(shouldRequestStatusAttestation(mockCredential)).toEqual(false); | ||
}); | ||
|
||
it("return true when the credential status is unknown", () => { | ||
jest.useFakeTimers().setSystemTime(new Date("2024-08-27T10:30:00+00:00")); | ||
|
||
const mockCredential: StoredCredential = { | ||
...baseMockCredential, | ||
storedStatusAttestation: { | ||
credentialStatus: "unknown" | ||
} | ||
}; | ||
expect(shouldRequestStatusAttestation(mockCredential)).toEqual(true); | ||
}); | ||
}); |
Oops, something went wrong.