feat(core): Add PKCE for OAuth2 (#6324)

* Remove authorization header when empty

* Import pkce

* Add OAuth2 with new grant type to Twitter

* Add pkce logic auto assign authorization code if pkce not defined

* Add pkce to ui and interfaces

* Fix scopes for Oauth2 twitter

* Deubg + pass it through header

* Add debug console, add airtable cred

* Remove all console.logs, make PKCE in th body only when it exists

* Remove invalid character ~

* Remove more console.logs

* remove body inside query

* Remove useless grantype check

* Hide oauth2 twitter waiting for overhaul

* Remove redundant header removal

* Remove more console.logs

* Add comment for code verifier

* Remove uneeded scopes

* Restore client id in callback

* Revert "Add OAuth2 with new grant type to Twitter"

This reverts commit 1c3b331aa1974159d1ffe1a4fbf2050722f0f24c.

* Remove oauth2 from twitter

* Remove properties linked to oauth2

* Fix lodash imports

* remove redundant check

* remove redundant codeVerifier

* patch pkce-challenge to avoid generating `code_verifier` with `~`

* store `codeVerifier` on the DB like `csrfSecret`

* remove unrelated changes

---------

Co-authored-by: Marcus <marcus@n8n.io>
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
agobrech
2023-06-21 10:54:32 +02:00
committed by GitHub
parent 4b0e0b7970
commit fc7261aca6
12 changed files with 130 additions and 12 deletions

View File

@@ -2,6 +2,7 @@ import type { ClientOAuth2Options } from '@n8n/client-oauth2';
import { ClientOAuth2 } from '@n8n/client-oauth2';
import Csrf from 'csrf';
import express from 'express';
import pkceChallenge from 'pkce-challenge';
import get from 'lodash/get';
import omit from 'lodash/omit';
import set from 'lodash/set';
@@ -142,6 +143,16 @@ oauth2CredentialController.get(
);
decryptedDataOriginal.csrfSecret = csrfSecret;
if (oauthCredentials.grantType === 'pkce') {
const { code_verifier, code_challenge } = pkceChallenge();
oAuthOptions.query = {
...oAuthOptions.query,
code_challenge,
code_challenge_method: 'S256',
};
decryptedDataOriginal.codeVerifier = code_verifier;
}
credentials.setData(decryptedDataOriginal, encryptionKey);
const newCredentialsData = credentials.getDataToSave() as unknown as ICredentialsDb;
@@ -189,7 +200,6 @@ oauth2CredentialController.get(
try {
// realmId it's currently just use for the quickbook OAuth2 flow
const { code, state: stateEncoded } = req.query;
if (!code || !stateEncoded) {
return renderCallbackError(
res,
@@ -265,12 +275,21 @@ oauth2CredentialController.get(
if ((get(oauthCredentials, 'authentication', 'header') as string) === 'body') {
options = {
body: {
client_id: get(oauthCredentials, 'clientId') as string,
client_secret: get(oauthCredentials, 'clientSecret', '') as string,
...(oauthCredentials.grantType === 'pkce' && {
code_verifier: decryptedDataOriginal.codeVerifier,
}),
...(oauthCredentials.grantType === 'authorizationCode' && {
client_id: get(oauthCredentials, 'clientId') as string,
client_secret: get(oauthCredentials, 'clientSecret', '') as string,
}),
},
};
// @ts-ignore
delete oAuth2Parameters.clientSecret;
} else if (oauthCredentials.grantType === 'pkce') {
options = {
body: { code_verifier: decryptedDataOriginal.codeVerifier },
};
}
await Container.get(ExternalHooks).run('oauth2.callback', [oAuth2Parameters]);