mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
🔧 feat: Add Basic Token Exchange Method for Actions OAuth flow (#7844)
- Enhanced the OAuth callback and action creation processes to include the `token_exchange_method` parameter. - Updated the `TokenService` to handle different token exchange methods, allowing for either 'default_post' or 'basic_auth_header' approaches. - Improved the handling of access tokens and refresh tokens based on the specified exchange method.
This commit is contained in:
parent
6488873bad
commit
1bd874591a
3 changed files with 38 additions and 12 deletions
|
|
@ -53,6 +53,7 @@ router.get('/:action_id/oauth/callback', async (req, res) => {
|
||||||
identifier,
|
identifier,
|
||||||
client_url: flowState.metadata.client_url,
|
client_url: flowState.metadata.client_url,
|
||||||
redirect_uri: flowState.metadata.redirect_uri,
|
redirect_uri: flowState.metadata.redirect_uri,
|
||||||
|
token_exchange_method: flowState.metadata.token_exchange_method,
|
||||||
/** Encrypted values */
|
/** Encrypted values */
|
||||||
encrypted_oauth_client_id: flowState.metadata.encrypted_oauth_client_id,
|
encrypted_oauth_client_id: flowState.metadata.encrypted_oauth_client_id,
|
||||||
encrypted_oauth_client_secret: flowState.metadata.encrypted_oauth_client_secret,
|
encrypted_oauth_client_secret: flowState.metadata.encrypted_oauth_client_secret,
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,7 @@ async function createActionTool({
|
||||||
userId: userId,
|
userId: userId,
|
||||||
client_url: metadata.auth.client_url,
|
client_url: metadata.auth.client_url,
|
||||||
redirect_uri: `${process.env.DOMAIN_SERVER}/api/actions/${action_id}/oauth/callback`,
|
redirect_uri: `${process.env.DOMAIN_SERVER}/api/actions/${action_id}/oauth/callback`,
|
||||||
|
token_exchange_method: metadata.auth.token_exchange_method,
|
||||||
/** Encrypted values */
|
/** Encrypted values */
|
||||||
encrypted_oauth_client_id: encrypted.oauth_client_id,
|
encrypted_oauth_client_id: encrypted.oauth_client_id,
|
||||||
encrypted_oauth_client_secret: encrypted.oauth_client_secret,
|
encrypted_oauth_client_secret: encrypted.oauth_client_secret,
|
||||||
|
|
@ -264,6 +265,7 @@ async function createActionTool({
|
||||||
refresh_token,
|
refresh_token,
|
||||||
client_url: metadata.auth.client_url,
|
client_url: metadata.auth.client_url,
|
||||||
encrypted_oauth_client_id: encrypted.oauth_client_id,
|
encrypted_oauth_client_id: encrypted.oauth_client_id,
|
||||||
|
token_exchange_method: metadata.auth.token_exchange_method,
|
||||||
encrypted_oauth_client_secret: encrypted.oauth_client_secret,
|
encrypted_oauth_client_secret: encrypted.oauth_client_secret,
|
||||||
});
|
});
|
||||||
const flowsCache = getLogStores(CacheKeys.FLOWS);
|
const flowsCache = getLogStores(CacheKeys.FLOWS);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
|
const { TokenExchangeMethodEnum } = require('librechat-data-provider');
|
||||||
const { handleOAuthToken } = require('~/models/Token');
|
const { handleOAuthToken } = require('~/models/Token');
|
||||||
const { decryptV2 } = require('~/server/utils/crypto');
|
const { decryptV2 } = require('~/server/utils/crypto');
|
||||||
const { logAxiosError } = require('~/utils');
|
const { logAxiosError } = require('~/utils');
|
||||||
|
|
@ -49,6 +50,7 @@ async function processAccessTokens(tokenData, { userId, identifier }) {
|
||||||
* @param {string} fields.client_url - The URL of the OAuth provider.
|
* @param {string} fields.client_url - The URL of the OAuth provider.
|
||||||
* @param {string} fields.identifier - The identifier for the token.
|
* @param {string} fields.identifier - The identifier for the token.
|
||||||
* @param {string} fields.refresh_token - The refresh token to use.
|
* @param {string} fields.refresh_token - The refresh token to use.
|
||||||
|
* @param {string} fields.token_exchange_method - The token exchange method ('default_post' or 'basic_auth_header').
|
||||||
* @param {string} fields.encrypted_oauth_client_id - The client ID for the OAuth provider.
|
* @param {string} fields.encrypted_oauth_client_id - The client ID for the OAuth provider.
|
||||||
* @param {string} fields.encrypted_oauth_client_secret - The client secret for the OAuth provider.
|
* @param {string} fields.encrypted_oauth_client_secret - The client secret for the OAuth provider.
|
||||||
* @returns {Promise<{
|
* @returns {Promise<{
|
||||||
|
|
@ -63,26 +65,36 @@ const refreshAccessToken = async ({
|
||||||
client_url,
|
client_url,
|
||||||
identifier,
|
identifier,
|
||||||
refresh_token,
|
refresh_token,
|
||||||
|
token_exchange_method,
|
||||||
encrypted_oauth_client_id,
|
encrypted_oauth_client_id,
|
||||||
encrypted_oauth_client_secret,
|
encrypted_oauth_client_secret,
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
const oauth_client_id = await decryptV2(encrypted_oauth_client_id);
|
const oauth_client_id = await decryptV2(encrypted_oauth_client_id);
|
||||||
const oauth_client_secret = await decryptV2(encrypted_oauth_client_secret);
|
const oauth_client_secret = await decryptV2(encrypted_oauth_client_secret);
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
Accept: 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
client_id: oauth_client_id,
|
|
||||||
client_secret: oauth_client_secret,
|
|
||||||
grant_type: 'refresh_token',
|
grant_type: 'refresh_token',
|
||||||
refresh_token,
|
refresh_token,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (token_exchange_method === TokenExchangeMethodEnum.BasicAuthHeader) {
|
||||||
|
const basicAuth = Buffer.from(`${oauth_client_id}:${oauth_client_secret}`).toString('base64');
|
||||||
|
headers['Authorization'] = `Basic ${basicAuth}`;
|
||||||
|
} else {
|
||||||
|
params.append('client_id', oauth_client_id);
|
||||||
|
params.append('client_secret', oauth_client_secret);
|
||||||
|
}
|
||||||
|
|
||||||
const response = await axios({
|
const response = await axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: client_url,
|
url: client_url,
|
||||||
headers: {
|
headers,
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
Accept: 'application/json',
|
|
||||||
},
|
|
||||||
data: params.toString(),
|
data: params.toString(),
|
||||||
});
|
});
|
||||||
await processAccessTokens(response.data, {
|
await processAccessTokens(response.data, {
|
||||||
|
|
@ -110,6 +122,7 @@ const refreshAccessToken = async ({
|
||||||
* @param {string} fields.identifier - The identifier for the token.
|
* @param {string} fields.identifier - The identifier for the token.
|
||||||
* @param {string} fields.client_url - The URL of the OAuth provider.
|
* @param {string} fields.client_url - The URL of the OAuth provider.
|
||||||
* @param {string} fields.redirect_uri - The redirect URI for the OAuth provider.
|
* @param {string} fields.redirect_uri - The redirect URI for the OAuth provider.
|
||||||
|
* @param {string} fields.token_exchange_method - The token exchange method ('default_post' or 'basic_auth_header').
|
||||||
* @param {string} fields.encrypted_oauth_client_id - The client ID for the OAuth provider.
|
* @param {string} fields.encrypted_oauth_client_id - The client ID for the OAuth provider.
|
||||||
* @param {string} fields.encrypted_oauth_client_secret - The client secret for the OAuth provider.
|
* @param {string} fields.encrypted_oauth_client_secret - The client secret for the OAuth provider.
|
||||||
* @returns {Promise<{
|
* @returns {Promise<{
|
||||||
|
|
@ -125,27 +138,37 @@ const getAccessToken = async ({
|
||||||
identifier,
|
identifier,
|
||||||
client_url,
|
client_url,
|
||||||
redirect_uri,
|
redirect_uri,
|
||||||
|
token_exchange_method,
|
||||||
encrypted_oauth_client_id,
|
encrypted_oauth_client_id,
|
||||||
encrypted_oauth_client_secret,
|
encrypted_oauth_client_secret,
|
||||||
}) => {
|
}) => {
|
||||||
const oauth_client_id = await decryptV2(encrypted_oauth_client_id);
|
const oauth_client_id = await decryptV2(encrypted_oauth_client_id);
|
||||||
const oauth_client_secret = await decryptV2(encrypted_oauth_client_secret);
|
const oauth_client_secret = await decryptV2(encrypted_oauth_client_secret);
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
Accept: 'application/json',
|
||||||
|
};
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
code,
|
code,
|
||||||
client_id: oauth_client_id,
|
|
||||||
client_secret: oauth_client_secret,
|
|
||||||
grant_type: 'authorization_code',
|
grant_type: 'authorization_code',
|
||||||
redirect_uri,
|
redirect_uri,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (token_exchange_method === TokenExchangeMethodEnum.BasicAuthHeader) {
|
||||||
|
const basicAuth = Buffer.from(`${oauth_client_id}:${oauth_client_secret}`).toString('base64');
|
||||||
|
headers['Authorization'] = `Basic ${basicAuth}`;
|
||||||
|
} else {
|
||||||
|
params.append('client_id', oauth_client_id);
|
||||||
|
params.append('client_secret', oauth_client_secret);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios({
|
const response = await axios({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
url: client_url,
|
url: client_url,
|
||||||
headers: {
|
headers,
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
Accept: 'application/json',
|
|
||||||
},
|
|
||||||
data: params.toString(),
|
data: params.toString(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue