Fix SAML signature
This commit is contained in:
		| @ -1,3 +1,17 @@ | |||||||
| # SAML2 auth plugin for PeerTube | # SAML2 auth plugin for PeerTube | ||||||
|  |  | ||||||
| Add SAML2 support to login form in 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`. | ||||||
|  | |||||||
| @ -29,14 +29,6 @@ async function register ({ | |||||||
|     default: metadataUrl |     default: metadataUrl | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   registerSetting({ |  | ||||||
|     name: 'sign-get-request', |  | ||||||
|     label: 'Sign get request', |  | ||||||
|     type: 'input-checkbox', |  | ||||||
|     private: true, |  | ||||||
|     default: false |  | ||||||
|   }) |  | ||||||
|  |  | ||||||
|   registerSetting({ |   registerSetting({ | ||||||
|     name: 'auth-display-name', |     name: 'auth-display-name', | ||||||
|     label: 'Auth display name', |     label: 'Auth display name', | ||||||
| @ -54,11 +46,33 @@ async function register ({ | |||||||
|  |  | ||||||
|   registerSetting({ |   registerSetting({ | ||||||
|     name: 'provider-certificate', |     name: 'provider-certificate', | ||||||
|     label: 'Identity provider certificate', |     label: 'Identity provider certificate (PEM format)', | ||||||
|     type: 'input-textarea', |     type: 'input-textarea', | ||||||
|     private: true |     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({ |   registerSetting({ | ||||||
|     name: 'username-property', |     name: 'username-property', | ||||||
|     label: 'Username property', |     label: 'Username property', | ||||||
| @ -145,7 +159,9 @@ async function loadSettingsAndCreateProviders ( | |||||||
|     'client-id', |     'client-id', | ||||||
|     'sign-get-request', |     'sign-get-request', | ||||||
|     'login-url', |     'login-url', | ||||||
|     'provider-certificate' |     'provider-certificate', | ||||||
|  |     'service-certificate', | ||||||
|  |     'service-private-key' | ||||||
|   ]) |   ]) | ||||||
|  |  | ||||||
|   if (!settings['login-url']) { |   if (!settings['login-url']) { | ||||||
| @ -158,12 +174,12 @@ async function loadSettingsAndCreateProviders ( | |||||||
|     return |     return | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const { publicKey: servicePublicKey, privateKey: servicePrivateKey } = await lazyLoadServiceCertificates(peertubeHelpers, storageManager) |   logger.debug('Creating SAML service/identity instances.', { settings }) | ||||||
|  |  | ||||||
|   const serviceOptions = { |   const serviceOptions = { | ||||||
|     entity_id: settings['client-id'], |     entity_id: settings['client-id'], | ||||||
|     private_key: servicePrivateKey, |     private_key: settings['service-private-key'], | ||||||
|     certificate: servicePublicKey, |     certificate: settings['service-certificate'], | ||||||
|     assert_endpoint: store.assertUrl |     assert_endpoint: store.assertUrl | ||||||
|   } |   } | ||||||
|   store.serviceProvider = new saml2.ServiceProvider(serviceOptions) |   store.serviceProvider = new saml2.ServiceProvider(serviceOptions) | ||||||
| @ -182,14 +198,19 @@ async function loadSettingsAndCreateProviders ( | |||||||
|     authName: 'saml2', |     authName: 'saml2', | ||||||
|     authDisplayName: () => store.authDisplayName, |     authDisplayName: () => store.authDisplayName, | ||||||
|     onAuthRequest: async (req, res) => { |     onAuthRequest: async (req, res) => { | ||||||
|       store.serviceProvider.create_login_request_url(store.identityProvider, {}, (err, loginUrl, requestId) => { |       try { | ||||||
|         if (err) { |         store.serviceProvider.create_login_request_url(store.identityProvider, {}, (err, loginUrl, requestId) => { | ||||||
|           logger.error('Cannot SAML 2 authenticate.', { err }) |           if (err) { | ||||||
|           return redirectOnError(res) |             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']) |     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 } |  | ||||||
| } |  | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user