import axios from 'axios' import CryptoJS from 'crypto-js' import apiConfig from '../config/api.config' // Just a disguise to obfuscate required tokens (including but not limited to client secret, // access tokens, and refresh tokens), used along with the following two functions const AES_SECRET_KEY = 'onedrive-vercel-index' export function obfuscateToken(token: string): string { // Encrypt token with AES const encrypted = CryptoJS.AES.encrypt(token, AES_SECRET_KEY) return encrypted.toString() } export function revealObfuscatedToken(obfuscated: string): string { // Decrypt SHA256 obfuscated token const decrypted = CryptoJS.AES.decrypt(obfuscated, AES_SECRET_KEY) return decrypted.toString(CryptoJS.enc.Utf8) } // Generate the Microsoft OAuth 2.0 authorization URL, used for requesting the authorisation code export function generateAuthorisationUrl(): string { const { clientId, redirectUri, authApi, scope } = apiConfig const authUrl = authApi.replace('/token', '/authorize') // Construct URL parameters for OAuth2 const params = new URLSearchParams() params.append('client_id', clientId) params.append('redirect_uri', redirectUri) params.append('response_type', 'code') params.append('scope', scope) params.append('response_mode', 'query') return `${authUrl}?${params.toString()}` } // The code returned from the Microsoft OAuth 2.0 authorization URL is a request URL with hostname // http://localhost and URL parameter code. This function extracts the code from the request URL export function extractAuthCodeFromRedirected(url: string): string { // Return empty string if the url is not the defined redirect uri if (!url.startsWith(apiConfig.redirectUri)) { return '' } // New URL search parameter const params = new URLSearchParams(url.split('?')[1]) return params.get('code') ?? '' } // After a successful authorisation, the code returned from the Microsoft OAuth 2.0 authorization URL // will be used to request an access token. This function requests the access token with the authorisation code // and returns the access token and refresh token on success. export async function requestTokenWithAuthCode( code: string ): Promise< | { expiryTime: string; accessToken: string; refreshToken: string } | { error: string; errorDescription: string; errorUri: string } > { const { clientId, redirectUri, authApi } = apiConfig const clientSecret = revealObfuscatedToken(apiConfig.obfuscatedClientSecret) // Construct URL parameters for OAuth2 const params = new URLSearchParams() params.append('client_id', clientId) params.append('redirect_uri', redirectUri) params.append('client_secret', clientSecret) params.append('code', code) params.append('grant_type', 'authorization_code') // Request access token return axios .post(authApi, params, { headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, }) .then(resp => { const { expires_in, access_token, refresh_token } = resp.data return { expiryTime: expires_in, accessToken: access_token, refreshToken: refresh_token } }) .catch(err => { const { error, error_description, error_uri } = err.response.data return { error, errorDescription: error_description, errorUri: error_uri } }) } // Verify the identity of the user with the access token and compare it with the userPrincipalName // in the Microsoft Graph API. If the userPrincipalName matches, proceed with token storing. export async function getAuthPersonInfo(accessToken: string) { const profileApi = apiConfig.driveApi.replace('/drive', '') return axios.get(profileApi, { headers: { Authorization: `Bearer ${accessToken}`, }, }) } export async function sendTokenToServer(accessToken: string, refreshToken: string, expiryTime: string) { return await axios.post( '/api', { obfuscatedAccessToken: obfuscateToken(accessToken), accessTokenExpiry: parseInt(expiryTime), obfuscatedRefreshToken: obfuscateToken(refreshToken), }, { headers: { 'Content-Type': 'application/json', }, } ) }