diff --git a/src/oidc-manager.js b/src/oidc-manager.js index 393176c..a4ce0f7 100644 --- a/src/oidc-manager.js +++ b/src/oidc-manager.js @@ -379,7 +379,7 @@ class OidcManager { } // Otherwise, verify that issuer is the preferred OIDC provider for the web id - return discoverProviderFor(webId) + return discoverProviderFor(webId, issuer) .then(preferredProvider => { if (preferredProvider === issuer) { // everything checks out return webId diff --git a/src/preferred-provider.js b/src/preferred-provider.js index b29ae02..169855c 100644 --- a/src/preferred-provider.js +++ b/src/preferred-provider.js @@ -61,20 +61,48 @@ function providerExists (uri) { * given Web ID, extracted from Link rel header or profile body. If no * provider URI was found, reject with an error. */ -function discoverProviderFor (webId) { +function discoverProviderFor (webId, issuer) { return discoverFromHeaders(webId) - .then(providerFromHeaders => providerFromHeaders || discoverFromProfile(webId)) + .then(providerFromHeaders => providerFromHeaders || discoverAllFromProfile(webId)) .then(providerUri => { - // drop the path (provider origin only) - if (providerUri) { - providerUri = (new URL(providerUri)).origin + if (Array.isArray(providerUri)) { + let list = providerUri + let lastErr = null + + for (let i = 0; i < list.length; i++) { + lastErr = null + providerUri = list[i] + if (providerUri) { + providerUri = (new URL(providerUri)).origin + } + + try { + validateProviderUri(providerUri, webId) // Throw an error if empty or invalid + } catch (err) { + lastErr = err + } + + if (lastErr === null && ((issuer && providerUri === issuer) || !issuer)) { + return providerUri + } + } + if (lastErr) { + throw lastErr + } else { + validateProviderUri(null, webId) // Throw an error if empty or invalid + } + } else { + // drop the path (provider origin only) + if (providerUri) { + providerUri = (new URL(providerUri)).origin + } + + validateProviderUri(providerUri, webId) // Throw an error if empty or invalid + + return providerUri } - - validateProviderUri(providerUri, webId) // Throw an error if empty or invalid - - return providerUri }) } @@ -94,16 +122,30 @@ function discoverFromHeaders (webId) { }) } -function discoverFromProfile (webId) { +function discoverAllFromProfile (webId) { const store = rdf.graph() const fetcher = rdf.fetcher(store) return fetcher.load(webId, { force: true }) .then(response => { + if (!response.ok) { + let error = new Error(`Could not reach Web ID ${webId} to discover provider`) + error.statusCode = 400 + throw error + } + let providerTerm = rdf.namedNode('http://www.w3.org/ns/solid/terms#oidcIssuer') - let providerUri = store.anyValue(rdf.namedNode(webId), providerTerm) - return providerUri + let idp = store.each(rdf.namedNode(webId), providerTerm, undefined) + let list = [] + + for (let i = 0; i < idp.length; i++) { + if (idp[i].uri) { + list.push(idp[i].uri) + } + } + + return list }, err => { let error = new Error(`Could not reach Web ID ${webId} to discover provider`) error.cause = err diff --git a/test/resources/sample-webid-profile1.js b/test/resources/sample-webid-profile1.js new file mode 100644 index 0000000..e4529e6 --- /dev/null +++ b/test/resources/sample-webid-profile1.js @@ -0,0 +1,20 @@ +module.exports = ` +@prefix solid: . +@prefix foaf: . +@prefix pim: . +@prefix schema: . +@prefix ldp: . + +<> + a foaf:PersonalProfileDocument ; + foaf:primaryTopic <#me> . + +<#me> + a schema:Person ; + + solid:account ; # link to the account uri + pim:storage ; # root storage + + solid:oidcIssuer ; + solid:oidcIssuer . +` diff --git a/test/unit/preferred-provider-test.js b/test/unit/preferred-provider-test.js index 7963fb8..72d99d7 100644 --- a/test/unit/preferred-provider-test.js +++ b/test/unit/preferred-provider-test.js @@ -64,6 +64,23 @@ describe('preferred-provider.js', () => { }) }) + it('should extract and validate the provider uri from the webid profile with multi oidcIssuer', () => { + nock(serverUri) + .options('/') + .reply(204, 'No content') + + nock(serverUri) + .get('/') + .reply(200, sampleProfileSrc, { + 'Content-Type': 'text/turtle' + }) + + return provider.discoverProviderFor(webId, 'https://provider.com') + .then(providerUri => { + expect(providerUri).to.equal('https://provider.com') + }) + }) + it('should throw an error if webid is reachable but no provider uri found', done => { nock(serverUri) .options('/')