@@ -81,25 +81,69 @@ router.get('/authorize', asyncHandler(async (req, res) => {
8181 res . redirect ( redirectUrl . toString ( ) ) ;
8282} ) ) ;
8383
84+ /**
85+ * Extract client credentials from request.
86+ * Supports both:
87+ * 1. HTTP Basic Authentication (Authorization header) - OAuth2 recommended
88+ * 2. Request body parameters (client_id, client_secret)
89+ */
90+ const extractClientCredentials = ( req ) => {
91+ // Method 1: Try Basic Auth header first (OAuth2 recommended)
92+ const authHeader = req . headers . authorization ;
93+ if ( authHeader && authHeader . startsWith ( 'Basic ' ) ) {
94+ try {
95+ const base64Credentials = authHeader . slice ( 6 ) ; // Remove 'Basic ' prefix
96+ const credentials = Buffer . from ( base64Credentials , 'base64' ) . toString ( 'utf-8' ) ;
97+ const [ clientId , clientSecret ] = credentials . split ( ':' , 2 ) ;
98+
99+ if ( clientId && clientSecret ) {
100+ return { clientId, clientSecret } ;
101+ }
102+ } catch {
103+ // Invalid Basic Auth format, fall through to body method
104+ }
105+ }
106+
107+ // Method 2: Fall back to request body
108+ const { client_id, client_secret } = req . body ;
109+ if ( client_id && client_secret ) {
110+ return { clientId : client_id , clientSecret : client_secret } ;
111+ }
112+
113+ return null ;
114+ } ;
115+
84116/**
85117 * POST /oauth/token
86118 *
87119 * OAuth2 token endpoint. Exchanges authorization code for access token.
88120 * Validates client credentials and authorization code before issuing tokens.
121+ *
122+ * Supports client credentials via:
123+ * - HTTP Basic Authentication (Authorization: Basic <base64(client_id:client_secret)>) - Recommended
124+ * - Request body (client_id, client_secret) - form-encoded or JSON
89125 */
90- router . post ( '/token' , express . urlencoded ( { extended : true } ) , asyncHandler ( async ( req , res ) => {
91- const { grant_type, code, client_id , client_secret , redirect_uri } = req . body ;
126+ router . post ( '/token' , express . json ( ) , express . urlencoded ( { extended : true } ) , asyncHandler ( async ( req , res ) => {
127+ const { grant_type, code, redirect_uri } = req . body ;
92128
93129 // Only support authorization code grant
94130 if ( grant_type !== 'authorization_code' ) {
95131 throwBadRequest ( 'Unsupported grant_type' ) ;
96132 }
97133
134+ // Extract client credentials (supports Basic Auth or body)
135+ const credentials = extractClientCredentials ( req ) ;
136+ if ( ! credentials ) {
137+ throwBadRequest ( 'Client credentials required. Provide via Basic Auth header or request body (client_id, client_secret)' ) ;
138+ }
139+
140+ const { clientId, clientSecret } = credentials ;
141+
98142 // Validate client credentials
99- await validateClient ( client_id , client_secret ) ;
143+ await validateClient ( clientId , clientSecret ) ;
100144
101145 // Validate and exchange authorization code
102- const { userId, scope } = validateAuthCode ( code , client_id , redirect_uri ) ;
146+ const { userId, scope } = validateAuthCode ( code , clientId , redirect_uri ) ;
103147
104148 // Get user details for token issuance
105149 const db = getDb ( ) ;
0 commit comments