Skip to content

Commit

Permalink
Merge b6c3892 into 760b0c4
Browse files Browse the repository at this point in the history
  • Loading branch information
RubenVerborgh committed Jul 16, 2018
2 parents 760b0c4 + b6c3892 commit a0446a8
Show file tree
Hide file tree
Showing 10 changed files with 33 additions and 213 deletions.
22 changes: 2 additions & 20 deletions README.md
Expand Up @@ -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?

Expand Down Expand Up @@ -95,10 +90,6 @@ logout (storage?: Storage): Promise<void>

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
Expand All @@ -113,21 +104,12 @@ fetch: (url: RequestInfo, options?: Object) => Promise<Response>
### types

```
type webIdTlsSession = {
authType: WebIdTls,
idp: string,
webId: string
}
type webIdOidcSession = {
authType: WebIdOidc,
idp: string,
webId: string,
accessToken: string,
idToken: string
}
type session = webIdTlsSession | webIdOidcSession
```

## Logging in via the popup app
Expand Down
62 changes: 19 additions & 43 deletions src/api.js
Expand Up @@ -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 = {
Expand All @@ -28,33 +27,11 @@ const defaultLoginOptions = (): loginOptions => {
export const fetch = (url: RequestInfo, options?: Object): Promise<Response> =>
authnFetch(defaultStorage())(url, options)

async function firstSession(
storage: AsyncStorage,
authFns: Array<() => Promise<?Session>>
): Promise<?Session> {
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
): Promise<?Session> {
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
}
Expand All @@ -75,32 +52,31 @@ export async function popupLogin(options: loginOptions): Promise<?Session> {
export async function currentSession(
storage: AsyncStorage = defaultStorage()
): Promise<?Session> {
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(
storage: AsyncStorage = defaultStorage()
): Promise<void> {
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
case 'WebID-TLS':
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)
}
80 changes: 1 addition & 79 deletions src/api.spec.js
Expand Up @@ -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')
Expand All @@ -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')
Expand All @@ -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')
Expand Down Expand Up @@ -217,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',
Expand All @@ -244,10 +217,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')
Expand Down Expand Up @@ -299,30 +268,13 @@ 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)
// To test currentSession with WebID-OIDC it's easist to set up the OIDC RP
// 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')
Expand Down Expand Up @@ -392,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',
Expand All @@ -413,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',
Expand All @@ -438,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',
Expand All @@ -456,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',
Expand Down Expand Up @@ -508,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',
Expand All @@ -528,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',
Expand All @@ -538,7 +484,7 @@ describe('fetch', () => {

await saveHost(window.localStorage)({
url: 'third-party.com',
authType: 'WebID-OIDC'
requiresAuth: true
})

nock('https://third-party.com')
Expand All @@ -552,29 +498,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)
})
})
})
10 changes: 2 additions & 8 deletions src/authn-fetch.js
Expand Up @@ -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
}
}

Expand All @@ -48,11 +48,5 @@ const fetchWithCredentials = async (
url: RequestInfo,
options?: Object
): Promise<Response> => {
switch (session.authType) {
case 'WebID-OIDC':
return WebIdOidc.fetchWithCredentials(session)(url, options)
case 'WebID-TLS':
default:
return fetch(url, options)
}
return WebIdOidc.fetchWithCredentials(session)(url, options)
}

0 comments on commit a0446a8

Please sign in to comment.