diff --git a/src/discovery/Discovery.ts b/src/discovery/Discovery.ts index 6a090472d4..0b89e69c99 100644 --- a/src/discovery/Discovery.ts +++ b/src/discovery/Discovery.ts @@ -13,6 +13,7 @@ import type IdentitiesManager from '../identities/IdentitiesManager'; import type { IdentityData, IdentityId, + IdentitySignedClaim, ProviderId, ProviderIdentityClaimId, ProviderIdentityId, @@ -873,33 +874,61 @@ class Discovery { > = {}; let nextPaginationToken: ProviderPaginationToken | undefined; - while (true) { - ctx.timer.refresh(); - const iterator = provider.getClaimIdsPage( - authIdentityId, - identityId, - nextPaginationToken, - ); - nextPaginationToken = undefined; - for await (const wrapper of iterator) { - // This will: - // 1. throw if the getClaimIdsPage takes too much time - // 2. the rest of this loop iteration takes too much time - if (ctx.signal.aborted) { - throw ctx.signal.reason; + let identitySignedClaimGenerator: () => AsyncGenerator; + if (provider.preferGetClaimsPage) { + identitySignedClaimGenerator = async function* () { + const iterator = provider.getClaimIdsPage( + authIdentityId, + identityId, + nextPaginationToken, + ); + nextPaginationToken = undefined; + for await (const wrapper of iterator) { + // This will: + // 1. throw if the getClaimIdsPage takes too much time + // 2. the rest of this loop iteration takes too much time + if (ctx.signal.aborted) { + throw ctx.signal.reason; + } + const claimId = wrapper.claimId; + nextPaginationToken = wrapper.nextPaginationToken; + // Refresh timer in preparation for request + ctx.timer.refresh(); + const identitySignedClaim = await provider.getClaim( + authIdentityId, + claimId, + ); + if (identitySignedClaim == null) { + continue; + } + // Claims on an identity provider will always be node -> identity + yield identitySignedClaim; } - const claimId = wrapper.claimId; - nextPaginationToken = wrapper.nextPaginationToken; - // Refresh timer in preparation for request - ctx.timer.refresh(); - const identitySignedClaim = await provider.getClaim( + }; + } else { + identitySignedClaimGenerator = async function* () { + const iterator = provider.getClaimsPage( authIdentityId, - claimId, + identityId, + nextPaginationToken, ); - if (identitySignedClaim == null) { - continue; + nextPaginationToken = undefined; + for await (const { claim } of iterator) { + // This will: + // 1. throw if the getClaimIdsPage takes too much time + // 2. the rest of this loop iteration takes too much time + if (ctx.signal.aborted) { + throw ctx.signal.reason; + } + // Claims on an identity provider will always be node -> identity + yield claim; } - // Claims on an identity provider will always be node -> identity + }; + } + while (true) { + // Refresh before each request made with identitySignedClaimGenerator + ctx.timer.refresh(); + for await (const identitySignedClaim of identitySignedClaimGenerator()) { const claim = identitySignedClaim.claim; const data = claim.payload; // Verify the claim with the public key of the node diff --git a/src/identities/Provider.ts b/src/identities/Provider.ts index 4ad596063a..be0dbec6ff 100644 --- a/src/identities/Provider.ts +++ b/src/identities/Provider.ts @@ -29,6 +29,14 @@ abstract class Provider { */ public abstract readonly id: ProviderId; + /** + * Set to true if getClaimsPage method should be preferred instead claim iteration operations. + * This could be useful if the Provider subclass has a getClaimsPage implentation that is able to + * obtain both Claims and ClaimsIds with a single HTTP request. For example, if the Provider were to + * supply a GraphQL API, or if the webscraped page were to contain the contents of both. + */ + public readonly preferGetClaimsPage: boolean = false; + public getTokens: GetTokens; public getToken: GetToken; public putToken: PutToken; diff --git a/src/identities/providers/github/GitHubProvider.ts b/src/identities/providers/github/GitHubProvider.ts index 17ac689bc1..c40e87fd2c 100644 --- a/src/identities/providers/github/GitHubProvider.ts +++ b/src/identities/providers/github/GitHubProvider.ts @@ -22,6 +22,7 @@ import * as utils from '../../../utils'; class GitHubProvider extends Provider { public readonly id = 'github.com' as ProviderId; public readonly clientId: string; + public readonly preferGetClaimsPage: boolean = true; protected readonly apiUrl: string = 'https://api.github.com'; protected readonly gistFilename: string = 'cryptolink.txt';