diff --git a/peertube-plugin-auth-saml2/README.md b/peertube-plugin-auth-saml2/README.md index bcbf14f..acff230 100644 --- a/peertube-plugin-auth-saml2/README.md +++ b/peertube-plugin-auth-saml2/README.md @@ -1,3 +1,17 @@ # SAML2 auth plugin for PeerTube Add SAML2 support to login form in PeerTube. + +## Keycloak example + +### Signature + +If you want to sign get requests: + * Generate a certificate and private key: `openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem` + * Import `cert.pem` in keycloak SAML client + * Copy `cert.pem` and `key.pem` in PeerTube SAML plugin settings + * Check the *Sign get request* checkbox in PeerTube SAML plugin settings + +### Provider certificate + +You can find the public key on: `http://keycloak.example.com/auth/realms/{realm}/protocol/saml/descriptor`. diff --git a/peertube-plugin-auth-saml2/main.js b/peertube-plugin-auth-saml2/main.js index 33a5d99..1743f6b 100644 --- a/peertube-plugin-auth-saml2/main.js +++ b/peertube-plugin-auth-saml2/main.js @@ -29,14 +29,6 @@ async function register ({ default: metadataUrl }) - registerSetting({ - name: 'sign-get-request', - label: 'Sign get request', - type: 'input-checkbox', - private: true, - default: false - }) - registerSetting({ name: 'auth-display-name', label: 'Auth display name', @@ -54,11 +46,33 @@ async function register ({ registerSetting({ name: 'provider-certificate', - label: 'Identity provider certificate', + label: 'Identity provider certificate (PEM format)', type: 'input-textarea', private: true }) + registerSetting({ + name: 'service-certificate', + label: 'Service certificate (PEM format)', + type: 'input-textarea', + private: true + }) + + registerSetting({ + name: 'service-private-key', + label: 'Service private key (PEM format)', + type: 'input-textarea', + private: true + }) + + registerSetting({ + name: 'sign-get-request', + label: 'Sign get request', + type: 'input-checkbox', + private: true, + default: false + }) + registerSetting({ name: 'username-property', label: 'Username property', @@ -145,7 +159,9 @@ async function loadSettingsAndCreateProviders ( 'client-id', 'sign-get-request', 'login-url', - 'provider-certificate' + 'provider-certificate', + 'service-certificate', + 'service-private-key' ]) if (!settings['login-url']) { @@ -158,12 +174,12 @@ async function loadSettingsAndCreateProviders ( return } - const { publicKey: servicePublicKey, privateKey: servicePrivateKey } = await lazyLoadServiceCertificates(peertubeHelpers, storageManager) + logger.debug('Creating SAML service/identity instances.', { settings }) const serviceOptions = { entity_id: settings['client-id'], - private_key: servicePrivateKey, - certificate: servicePublicKey, + private_key: settings['service-private-key'], + certificate: settings['service-certificate'], assert_endpoint: store.assertUrl } store.serviceProvider = new saml2.ServiceProvider(serviceOptions) @@ -182,14 +198,19 @@ async function loadSettingsAndCreateProviders ( authName: 'saml2', authDisplayName: () => store.authDisplayName, onAuthRequest: async (req, res) => { - store.serviceProvider.create_login_request_url(store.identityProvider, {}, (err, loginUrl, requestId) => { - if (err) { - logger.error('Cannot SAML 2 authenticate.', { err }) - return redirectOnError(res) - } + try { + store.serviceProvider.create_login_request_url(store.identityProvider, {}, (err, loginUrl, requestId) => { + if (err) { + logger.error('Cannot SAML 2 authenticate.', { err }) + return redirectOnError(res) + } - res.redirect(loginUrl) - }) + res.redirect(loginUrl) + }) + } catch (err) { + logger.error('Cannot create login request url.', { err }) + return redirectOnError(res) + } } }) @@ -256,39 +277,3 @@ async function buildUser (settingsManager, samlUser) { role: findInUser(samlUser, settings['role-property']) } } - -async function lazyLoadServiceCertificates (peertubeHelpers, storageManager) { - const { logger } = peertubeHelpers - - let privateKey = await storageManager.getData('service-private-key') - let publicKey = await storageManager.getData('service-public-key') - - if (!privateKey || !publicKey) { - logger.info('Generating public/private keys for SAML 2.') - - return new Promise((res, rej) => { - const options = { - modulusLength: 2048, - publicKeyEncoding: { - type: 'spki', - format: 'pem' - }, - privateKeyEncoding: { - type: 'pkcs8', - format: 'pem' - } - } - - crypto.generateKeyPair('rsa', options, (err, publicKey, privateKey) => { - if (err) return rej(err) - - Promise.all([ - storageManager.storeData('service-private-key', privateKey), - storageManager.storeData('service-public-key', publicKey) - ]).then(() => res({ publicKey, privateKey })) - }) - }) - } - - return { privateKey, publicKey } -}