Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(xo-server-auth-oidc): OpenID Connect authentication plugin #6684

Merged
merged 1 commit into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.unreleased.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

- [VM/Advanced] Warning message when enabling Windows update tools [#6627](https://github.com/vatesfr/xen-orchestra/issues/6627) (PR [#6681](https://github.com/vatesfr/xen-orchestra/issues/6681))
- [Continuous Replication] : add HealthCheck support to Continuous Replication (PR [#6668](https://github.com/vatesfr/xen-orchestra/pull/6668))
- [Plugin/auth-oidc] [OpenID Connect](<https://en.wikipedia.org/wiki/OpenID#OpenID_Connect_(OIDC)>) authentication plugin [#6641](https://github.com/vatesfr/xen-orchestra/issues/6641) (PR [#6684](https://github.com/vatesfr/xen-orchestra/issues/6684))

### Bug fixes

Expand Down Expand Up @@ -37,6 +38,7 @@
- @xen-orchestra/backups minor
- xen-api patch
- xo-cli minor
- xo-server-auth-oidc minor
- xo-web minor
- xo-server patch

Expand Down
11 changes: 11 additions & 0 deletions packages/xo-server-auth-oidc/.USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
This plugin allows users to authenticate to Xen-Orchestra using [OpenID Connect](<https://en.wikipedia.org/wiki/OpenID#OpenID_Connect_(OIDC)>).

The first time a user signs in, XO will create a new XO user with the
same identifier.

Like all other xo-server plugins, it can be configured directly via
the web interface, see [the plugin documentation](https://xen-orchestra.com/docs/plugins.html).

> Important: When registering your instance to your identity provider,
> you must configure its callback URL to
> `http://xo.company.net/signin/oidc/callback`!
1 change: 1 addition & 0 deletions packages/xo-server-auth-oidc/.npmignore
32 changes: 32 additions & 0 deletions packages/xo-server-auth-oidc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!-- DO NOT EDIT MANUALLY, THIS FILE HAS BEEN GENERATED -->

# xo-server-auth-oidc

## Usage

This plugin allows users to authenticate to Xen-Orchestra using [OpenID Connect](<https://en.wikipedia.org/wiki/OpenID#OpenID_Connect_(OIDC)>).

The first time a user signs in, XO will create a new XO user with the
same identifier.

Like all other xo-server plugins, it can be configured directly via
the web interface, see [the plugin documentation](https://xen-orchestra.com/docs/plugins.html).

> Important: When registering your instance to your identity provider,
> you must configure its callback URL to
> `http://xo.company.net/signin/oidc/callback`!

## Contributions

Contributions are _very_ welcomed, either on the documentation or on
the code.

You may:

- report any [issue](https://github.com/vatesfr/xen-orchestra/issues)
you've encountered;
- fork and create a pull request.

## License

[AGPL-3.0-or-later](https://spdx.org/licenses/AGPL-3.0-or-later) © [Vates SAS](https://vates.fr)
101 changes: 101 additions & 0 deletions packages/xo-server-auth-oidc/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
'use strict'

const { Strategy } = require('passport-openidconnect')

// ===================================================================

const DISCOVERABLE_SETTINGS = ['authorizationURL', 'issuer', 'userInfoURL', 'tokenURL']

exports.configurationSchema = {
type: 'object',
properties: {
discoveryURL: {
description: 'If this field is not used, you will need to manually enter settings in the *Advanced* section.',
title: 'Auto-discovery URL',
type: 'string',
},
clientID: { title: 'Client identifier (key)', type: 'string' },
clientSecret: { title: 'Client secret', type: 'string' },

advanced: {
title: 'Advanced',
type: 'object',
properties: {
authorizationURL: { title: 'Authorization URL', type: 'string' },
callbackURL: {
description: 'Default to https://<xo.company.net>/signin/oidc/callback`.',
title: 'Callback URL',
type: 'string',
},
issuer: { title: 'Issuer', type: 'string' },
tokenURL: { title: 'Token URL', type: 'string' },
userInfoURL: { title: 'User info URL', type: 'string' },
usernameField: {
default: 'username',
description: 'Field to use as the XO username',
title: 'Username field',
type: 'string',
},
},
},
},
required: ['clientID', 'clientSecret'],
anyOf: [{ required: ['discoveryURL'] }, { properties: { advanced: { required: DISCOVERABLE_SETTINGS } } }],
}

// ===================================================================

class AuthOidc {
#conf
#unregisterPassportStrategy
#xo

constructor(xo) {
this.#xo = xo
}

async configure({ advanced, ...conf }, { loaded }) {
this.#conf = { callbackURL: '/signin/oidc/callback', ...advanced, ...conf }

if (loaded) {
await this.unload()
await this.load()
}
}

async load() {
const xo = this.#xo
const { discoveryURL, usernameField, ...conf } = this.#conf

if (discoveryURL !== undefined) {
const res = await this.#xo.httpRequest(discoveryURL)
const data = await res.json()

for (const key of DISCOVERABLE_SETTINGS) {
if (!conf[key]) {
conf[key] = data[key.endsWith('URL') ? key.slice(0, -3).toLowerCase() + '_endpoint' : key]
}
}
}

this.#unregisterPassportStrategy = xo.registerPassportStrategy(
new Strategy(conf, async (issuer, profile, done) => {
try {
const { id, [usernameField]: name } = profile
done(null, await xo.registerUser2('oidc:' + issuer, { user: { id, name } }))
} catch (error) {
done(error.message)
}
}),
{ label: 'OpenID Connect', name: 'oidc' }
)
}

unload() {
this.#unregisterPassportStrategy()
}
}

// ===================================================================

exports.default = ({ xo }) => new AuthOidc(xo)
23 changes: 23 additions & 0 deletions packages/xo-server-auth-oidc/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"private": true,
"name": "xo-server-auth-oidc",
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/xo-server-auth-oidc",
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
"repository": {
"directory": "packages/xo-server-auth-oidc",
"type": "git",
"url": "https://github.com/vatesfr/xen-orchestra.git"
},
"author": {
"name": "Vates SAS",
"url": "https://vates.fr"
},
"license": "AGPL-3.0-or-later",
"version": "0.0.0",
"engines": {
"node": ">=12"
},
"dependencies": {
"passport-openidconnect": "^0.1.1"
}
}
8 changes: 8 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15061,6 +15061,14 @@ passport-oauth2@1.x.x:
uid2 "0.0.x"
utils-merge "1.x.x"

passport-openidconnect@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/passport-openidconnect/-/passport-openidconnect-0.1.1.tgz#83921ff5f87f634079f65262dada834af1972244"
integrity sha512-r0QJiWEzwCg2MeCIXVP5G6YxVRqnEsZ2HpgKRthZ9AiQHJrgGUytXpsdcGF9BRwd3yMrEesb/uG/Yxb86rrY0g==
dependencies:
oauth "0.9.x"
passport-strategy "1.x.x"

passport-saml@^3.2.0:
version "3.2.4"
resolved "https://registry.yarnpkg.com/passport-saml/-/passport-saml-3.2.4.tgz#e8e9523f954988a3a47d12e425d7fa0f20a74dc9"
Expand Down