From d45b7b6cae9cccc7f0e55130539923c3b9119d00 Mon Sep 17 00:00:00 2001 From: Ruben Verborgh Date: Sun, 15 Jul 2018 19:05:07 -0400 Subject: [PATCH 1/4] Remove WebID-TLS support. --- src/api.js | 6 ---- src/api.spec.js | 71 ---------------------------------------------- src/authn-fetch.js | 1 - src/host.js | 3 -- src/popup.spec.js | 2 -- src/session.js | 10 ++----- src/types.js | 4 +-- src/webid-tls.js | 23 --------------- 8 files changed, 3 insertions(+), 117 deletions(-) delete mode 100644 src/webid-tls.js diff --git a/src/api.js b/src/api.js index 964d4e4..10d3545 100644 --- a/src/api.js +++ b/src/api.js @@ -7,7 +7,6 @@ import { getSession, saveSession, clearSession } from './session' import type { AsyncStorage } from './storage' import { defaultStorage } from './storage' import { currentUrlNoParams } from './url-util' -import * as WebIdTls from './webid-tls' import * as WebIdOidc from './webid-oidc' export type loginOptions = { @@ -51,10 +50,6 @@ export async function login( options: loginOptions ): Promise { options = { ...defaultLoginOptions(), ...options } - const webIdTlsSession = await WebIdTls.login(idp) - if (webIdTlsSession) { - return saveSession(options.storage)(webIdTlsSession) - } const webIdOidcLogin = await WebIdOidc.login(idp, options) return webIdOidcLogin } @@ -98,7 +93,6 @@ export async function logout( console.error(err) } break - case 'WebID-TLS': default: break } diff --git a/src/api.spec.js b/src/api.spec.js index 3d7dc89..49353e1 100644 --- a/src/api.spec.js +++ b/src/api.spec.js @@ -103,28 +103,10 @@ describe('login', () => { expect(await getStoredSession()).toBeNull() }) - describe('WebID-TLS', () => { - it('can log in with WebID-TLS', async () => { - expect.assertions(2) - const webId = 'https://localhost/profile#me' - nock('https://localhost/') - .head('/') - .reply(200, '', { user: webId }) - - const session = await login('https://localhost') - expect(session.webId).toBe(webId) - expect(await getStoredSession()).toEqual(session) - }) - }) - describe('WebID-OIDC', () => { it('can log in with WebID-OIDC', async () => { expect.assertions(6) nock('https://localhost/') - // try to log in with WebID-TLS - .head('/') - .reply(200) - // no user header, so try to use WebID-OIDC .get('/.well-known/openid-configuration') .reply(200, oidcConfiguration) .get('/jwks') @@ -149,10 +131,6 @@ describe('login', () => { it('uses the provided redirect uri', async () => { expect.assertions(6) nock('https://localhost') - // try to log in with WebID-TLS - .head('/') - .reply(200) - // no user header, so try to use WebID-OIDC .get('/.well-known/openid-configuration') .reply(200, oidcConfiguration) .get('/jwks') @@ -179,10 +157,6 @@ describe('login', () => { it('strips the hash fragment from the current URL when providing the default redirect URL', async () => { expect.assertions(6) nock('https://localhost/') - // try to log in with WebID-TLS - .head('/') - .reply(200) - // no user header, so try to use WebID-OIDC .get('/.well-known/openid-configuration') .reply(200, oidcConfiguration) .get('/jwks') @@ -244,10 +218,6 @@ describe('currentSession', () => { // client by logging in, generating the IDP's response, and redirecting // back to the app. nock('https://localhost/') - // try to log in with WebID-TLS - .head('/') - .reply(200) - // no user header, so try to use WebID-OIDC .get('/.well-known/openid-configuration') .reply(200, oidcConfiguration) .get('/jwks') @@ -299,19 +269,6 @@ describe('currentSession', () => { }) describe('logout', () => { - describe('WebID-TLS', () => { - it('just removes the current session from the store', async () => { - expect.assertions(1) - await saveSession(window.localStorage)({ - authType: 'WebID-TLS', - idp: 'https://localhost', - webId: 'https://person.me/#me' - }) - await logout() - expect(await getStoredSession()).toBeNull() - }) - }) - describe('WebID-OIDC', () => { it('hits the end_session_endpoint and clears the current session from the store', async () => { expect.assertions(7) @@ -319,10 +276,6 @@ describe('logout', () => { // client by logging in, generating the IDP's response, and redirecting // back to the app. nock('https://localhost/') - // try to log in with WebID-TLS - .head('/') - .reply(200) - // no user header, so try to use WebID-OIDC .get('/.well-known/openid-configuration') .reply(200, oidcConfiguration) .get('/jwks') @@ -552,29 +505,5 @@ describe('fetch', () => { const resp = await fetch('https://third-party.com/resource') expect(resp.status).toBe(200) }) - - it('does not send credentials to a familiar domain when that domain uses a different auth type', async () => { - expect.assertions(1) - await saveSession(window.localStorage)({ - authType: 'WebID-OIDC', - idp: 'https://localhost', - webId: 'https://person.me/#me', - accessToken: 'fake_access_token', - idToken: 'abc.def.ghi', - sessionKey - }) - - await saveHost(window.localStorage)({ - url: 'third-party.com', - authType: 'WebID-TLS' - }) - - nock('https://third-party.com') - .get('/resource') - .reply(401) - - const resp = await fetch('https://third-party.com/resource') - expect(resp.status).toBe(401) - }) }) }) diff --git a/src/authn-fetch.js b/src/authn-fetch.js index b7db5e9..0c6d03a 100644 --- a/src/authn-fetch.js +++ b/src/authn-fetch.js @@ -51,7 +51,6 @@ const fetchWithCredentials = async ( switch (session.authType) { case 'WebID-OIDC': return WebIdOidc.fetchWithCredentials(session)(url, options) - case 'WebID-TLS': default: return fetch(url, options) } diff --git a/src/host.js b/src/host.js index 665d587..6163751 100644 --- a/src/host.js +++ b/src/host.js @@ -5,7 +5,6 @@ import type { AsyncStorage } from './storage' import { getData, updateStorage } from './storage' import type { Auth } from './types' import * as WebIdOidc from './webid-oidc' -import * as WebIdTls from './webid-tls' export type host = { authType: Auth, @@ -57,8 +56,6 @@ export function updateHostFromResponse( let authType if (WebIdOidc.requiresAuth(resp)) { authType = 'WebID-OIDC' - } else if (WebIdTls.requiresAuth(resp)) { - authType = 'WebID-TLS' } else { authType = null } diff --git a/src/popup.spec.js b/src/popup.spec.js index 85418e2..c883305 100644 --- a/src/popup.spec.js +++ b/src/popup.spec.js @@ -96,7 +96,6 @@ describe('loginHandler', () => { const mockCallback = jest.fn() const handler = loginHandler(options, mockCallback) const session = { - authType: 'WebID-TLS', idp: 'https://example.com', webId: 'https://me.example.com/profile#me' } @@ -167,7 +166,6 @@ describe('startPopupServer', () => { expect.assertions(1) const store = defaultStorage() const session = { - authType: 'WebIdTls', idp: 'https://localhost', webId: 'https://localhost/profile#me' } diff --git a/src/session.js b/src/session.js index f8d2d50..442b1cd 100644 --- a/src/session.js +++ b/src/session.js @@ -2,13 +2,7 @@ import type { AsyncStorage } from './storage' import { getData, updateStorage } from './storage' -import type { WebIdTls, WebIdOidc } from './types' - -export type webIdTlsSession = { - authType: WebIdTls, - idp: string, - webId: string -} +import type { WebIdOidc } from './types' export type webIdOidcSession = { authType: WebIdOidc, @@ -20,7 +14,7 @@ export type webIdOidcSession = { sessionKey: string } -export type Session = webIdTlsSession | webIdOidcSession +export type Session = webIdOidcSession export async function getSession(storage: AsyncStorage): Promise { const data = await getData(storage) diff --git a/src/types.js b/src/types.js index 6e62e90..1d8b0c6 100644 --- a/src/types.js +++ b/src/types.js @@ -3,6 +3,4 @@ // Canonical auth protocol names export type WebIdOidc = 'WebID-OIDC' -export type WebIdTls = 'WebID-TLS' - -export type Auth = WebIdOidc | WebIdTls +export type Auth = WebIdOidc diff --git a/src/webid-tls.js b/src/webid-tls.js deleted file mode 100644 index 8ab481e..0000000 --- a/src/webid-tls.js +++ /dev/null @@ -1,23 +0,0 @@ -// @flow -/* global fetch, Response */ -import 'isomorphic-fetch' - -import * as authorization from 'auth-header' -import type { webIdTlsSession } from './session' - -export const login = (idp: string): Promise => - fetch(idp, { method: 'HEAD', credentials: 'include' }) - .then(resp => resp.headers.get('user')) - .then(webId => (webId ? { authType: 'WebID-TLS', idp, webId } : null)) - -export const requiresAuth = (resp: Response): boolean => { - if (resp.status !== 401) { - return false - } - const wwwAuthHeader = resp.headers.get('www-authenticate') - if (!wwwAuthHeader) { - return false - } - const auth = authorization.parse(wwwAuthHeader) - return auth.scheme === 'WebID-TLS' -} From fb597ec1150066dd0a0529e734408cc017a845dd Mon Sep 17 00:00:00 2001 From: Ruben Verborgh Date: Sun, 15 Jul 2018 19:07:27 -0400 Subject: [PATCH 2/4] Remove TLS from README. --- README.md | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index cc7de7c..cd6b1fe 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,8 @@ Opaquely authenticates [Solid](https://github.com/solid/) clients ## About ### What is this? - -Solid currently supports two cross-origin authentication protocols, -[WebID-TLS](https://www.w3.org/2005/Incubator/webid/spec/tls/) and -[WebID-OIDC](https://github.com/solid/webid-oidc-spec). - -This library abstracts away the implementation details of these specs so that -clients don't have to handle different authentication protocols. +This library facilitates authentication with Solid servers +by implementing [WebID-OIDC](https://github.com/solid/webid-oidc-spec). ### Why might I need this? @@ -95,10 +90,6 @@ logout (storage?: Storage): Promise Clears the active user session. -WARNING: this is an unsupported use case in WebID-TLS. Once your browser -provides its client cert to a web server, there's no going back! So for -WebID-TLS, the only thing this will do is clear the session from the store. - ### `fetch` Fetches a resource from the web. Same API as @@ -113,12 +104,6 @@ fetch: (url: RequestInfo, options?: Object) => Promise ### types ``` -type webIdTlsSession = { - authType: WebIdTls, - idp: string, - webId: string -} - type webIdOidcSession = { authType: WebIdOidc, idp: string, @@ -126,8 +111,6 @@ type webIdOidcSession = { accessToken: string, idToken: string } - -type session = webIdTlsSession | webIdOidcSession ``` ## Logging in via the popup app From c8b2c5d33cb63278a5b400da4a6ebfa5b7ad1f7f Mon Sep 17 00:00:00 2001 From: Ruben Verborgh Date: Sun, 15 Jul 2018 21:09:31 -0400 Subject: [PATCH 3/4] Remove authType. The authType field indicated whether TLS or OIDC was used. Since d45b7b6cae9cccc7f0e55130539923c3b9119d00 removed TLS, auth is necessarily OIDC. --- README.md | 1 - src/api.js | 23 ++++++++--------------- src/api.spec.js | 9 +-------- src/authn-fetch.js | 9 ++------- src/host.js | 25 ++++++++----------------- src/session.js | 2 -- src/types.js | 6 ------ src/webid-oidc.js | 1 - 8 files changed, 19 insertions(+), 57 deletions(-) delete mode 100644 src/types.js diff --git a/README.md b/README.md index cd6b1fe..33b0017 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,6 @@ fetch: (url: RequestInfo, options?: Object) => Promise ``` type webIdOidcSession = { - authType: WebIdOidc, idp: string, webId: string, accessToken: string, diff --git a/src/api.js b/src/api.js index 10d3545..6e9c7d2 100644 --- a/src/api.js +++ b/src/api.js @@ -81,20 +81,13 @@ export async function logout( storage: AsyncStorage = defaultStorage() ): Promise { const session = await getSession(storage) - if (!session) { - return - } - switch (session.authType) { - case 'WebID-OIDC': - try { - await WebIdOidc.logout(storage) - } catch (err) { - console.warn('Error logging out:') - console.error(err) - } - break - default: - break + if (session) { + try { + await WebIdOidc.logout(storage) + } catch (err) { + console.warn('Error logging out:') + console.error(err) + } + await clearSession(storage) } - return clearSession(storage) } diff --git a/src/api.spec.js b/src/api.spec.js index 49353e1..12b06fe 100644 --- a/src/api.spec.js +++ b/src/api.spec.js @@ -191,7 +191,6 @@ describe('currentSession', () => { it('can find the current session if stored', async () => { expect.assertions(2) await saveSession(window.localStorage)({ - authType: 'WebID-OIDC', idp: 'https://localhost', webId: 'https://person.me/#me', accessToken: 'fake_access_token', @@ -345,7 +344,6 @@ describe('fetch', () => { it('handles 401s from WebID-OIDC resources by resending with credentials', async () => { expect.assertions(1) await saveSession(window.localStorage)({ - authType: 'WebID-OIDC', idp: 'https://localhost', webId: 'https://person.me/#me', accessToken: 'fake_access_token', @@ -366,7 +364,6 @@ describe('fetch', () => { it('merges request headers with the authorization header', async () => { await saveSession(window.localStorage)({ - authType: 'WebID-OIDC', idp: 'https://localhost', webId: 'https://person.me/#me', accessToken: 'fake_access_token', @@ -391,7 +388,6 @@ describe('fetch', () => { it('does not resend with credentials if the www-authenticate header is missing', async () => { expect.assertions(1) await saveSession(window.localStorage)({ - authType: 'WebID-OIDC', idp: 'https://localhost', webId: 'https://person.me/#me', accessToken: 'fake_access_token', @@ -409,7 +405,6 @@ describe('fetch', () => { it('does not resend with credentials if the www-authenticate header suggests an unknown scheme', async () => { await saveSession(window.localStorage)({ - authType: 'WebID-OIDC', idp: 'https://localhost', webId: 'https://person.me/#me', accessToken: 'fake_access_token', @@ -461,7 +456,6 @@ describe('fetch', () => { it('just sends one request when the RP is also the IDP', async () => { expect.assertions(1) await saveSession(window.localStorage)({ - authType: 'WebID-OIDC', idp: 'https://localhost', webId: 'https://person.me/#me', accessToken: 'fake_access_token', @@ -481,7 +475,6 @@ describe('fetch', () => { it('just sends one request to domains it has already encountered', async () => { expect.assertions(1) await saveSession(window.localStorage)({ - authType: 'WebID-OIDC', idp: 'https://localhost', webId: 'https://person.me/#me', accessToken: 'fake_access_token', @@ -491,7 +484,7 @@ describe('fetch', () => { await saveHost(window.localStorage)({ url: 'third-party.com', - authType: 'WebID-OIDC' + requiresAuth: true }) nock('https://third-party.com') diff --git a/src/authn-fetch.js b/src/authn-fetch.js index 0c6d03a..6dc12a8 100644 --- a/src/authn-fetch.js +++ b/src/authn-fetch.js @@ -39,7 +39,7 @@ function shouldShareCredentials( return false } const requestHost = await getHost(storage)(url) - return requestHost != null && session.authType === requestHost.authType + return requestHost != null && requestHost.requiresAuth } } @@ -48,10 +48,5 @@ const fetchWithCredentials = async ( url: RequestInfo, options?: Object ): Promise => { - switch (session.authType) { - case 'WebID-OIDC': - return WebIdOidc.fetchWithCredentials(session)(url, options) - default: - return fetch(url, options) - } + return WebIdOidc.fetchWithCredentials(session)(url, options) } diff --git a/src/host.js b/src/host.js index 6163751..962de97 100644 --- a/src/host.js +++ b/src/host.js @@ -3,12 +3,11 @@ import { getSession } from './session' import type { AsyncStorage } from './storage' import { getData, updateStorage } from './storage' -import type { Auth } from './types' import * as WebIdOidc from './webid-oidc' export type host = { - authType: Auth, - url: string + url: string, + requiresAuth: boolean } export const hostNameFromRequestInfo = (url: RequestInfo): string => { @@ -26,7 +25,7 @@ export function getHost(storage: AsyncStorage): RequestInfo => Promise { const requestHostName = hostNameFromRequestInfo(url) const session = await getSession(storage) if (session && hostNameFromRequestInfo(session.idp) === requestHostName) { - return { url: requestHostName, authType: session.authType } + return { url: requestHostName, requiresAuth: true } } const { hosts } = await getData(storage) if (!hosts) { @@ -36,16 +35,15 @@ export function getHost(storage: AsyncStorage): RequestInfo => Promise { } } -export function saveHost(storage: AsyncStorage): host => Promise { - return async ({ url, authType }) => { +export function saveHost(storage: AsyncStorage): host => Promise { + return async ({ url, requiresAuth }) => { await updateStorage(storage, data => ({ ...data, hosts: { ...data.hosts, - [url]: { authType } + [url]: { requiresAuth } } })) - return { url, authType } } } @@ -53,16 +51,9 @@ export function updateHostFromResponse( storage: AsyncStorage ): Response => Promise { return async resp => { - let authType if (WebIdOidc.requiresAuth(resp)) { - authType = 'WebID-OIDC' - } else { - authType = null - } - - const hostName = hostNameFromRequestInfo(resp.url) - if (authType) { - await saveHost(storage)({ url: hostName, authType }) + const hostName = hostNameFromRequestInfo(resp.url) + await saveHost(storage)({ url: hostName, requiresAuth: true }) } } } diff --git a/src/session.js b/src/session.js index 442b1cd..4849c8c 100644 --- a/src/session.js +++ b/src/session.js @@ -2,10 +2,8 @@ import type { AsyncStorage } from './storage' import { getData, updateStorage } from './storage' -import type { WebIdOidc } from './types' export type webIdOidcSession = { - authType: WebIdOidc, idp: string, webId: string, accessToken: string, diff --git a/src/types.js b/src/types.js deleted file mode 100644 index 1d8b0c6..0000000 --- a/src/types.js +++ /dev/null @@ -1,6 +0,0 @@ -// @flow - -// Canonical auth protocol names -export type WebIdOidc = 'WebID-OIDC' - -export type Auth = WebIdOidc diff --git a/src/webid-oidc.js b/src/webid-oidc.js index 931be8b..b79cae9 100644 --- a/src/webid-oidc.js +++ b/src/webid-oidc.js @@ -46,7 +46,6 @@ export const currentSession = async ( await restoreAppHashFragment(storage) const { idp, idToken, accessToken, clientId, sessionKey } = resp return { - authType: 'WebID-OIDC', webId: resp.decoded.payload.sub, idp, idToken, From ba30fab5dfbb75fdd561cc8010a0cf6059233b36 Mon Sep 17 00:00:00 2001 From: Ruben Verborgh Date: Sun, 15 Jul 2018 21:18:11 -0400 Subject: [PATCH 4/4] Remove firstSession function. --- src/api.js | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/api.js b/src/api.js index 6e9c7d2..1e5426e 100644 --- a/src/api.js +++ b/src/api.js @@ -27,24 +27,6 @@ const defaultLoginOptions = (): loginOptions => { export const fetch = (url: RequestInfo, options?: Object): Promise => authnFetch(defaultStorage())(url, options) -async function firstSession( - storage: AsyncStorage, - authFns: Array<() => Promise> -): Promise { - if (authFns.length === 0) { - return null - } - try { - const session = await authFns[0]() - if (session) { - return saveSession(storage)(session) - } - } catch (err) { - console.error(err) - } - return firstSession(storage, authFns.slice(1)) -} - export async function login( idp: string, options: loginOptions @@ -70,11 +52,18 @@ export async function popupLogin(options: loginOptions): Promise { export async function currentSession( storage: AsyncStorage = defaultStorage() ): Promise { - const session = await getSession(storage) - if (session) { - return session + let session = await getSession(storage) + if (!session) { + try { + session = await WebIdOidc.currentSession(storage) + } catch (err) { + console.error(err) + } + if (session) { + await saveSession(storage)(session) + } } - return firstSession(storage, [WebIdOidc.currentSession.bind(null, storage)]) + return session } export async function logout(