mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
ready to test
Testing Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
/***********************************************************************************************************************************************************************
|
||||
* File Name: google_oidc.util.js
|
||||
* Type of Program: Utility
|
||||
* Description: Manual Google OIDC primitives — no Passport.
|
||||
* Provides state/nonce generation, PKCE (S256), authorization URL construction,
|
||||
* authorization code exchange, and ID token verification via google-auth-library.
|
||||
*
|
||||
* OIDC flow summary:
|
||||
* 1. googleRedirect → generateState + generateNonce + generatePKCE → buildAuthUrl → redirect
|
||||
* 2. googleCallback → verify state cookie → exchangeCode → verifyIdToken → create/find user
|
||||
*
|
||||
* Security properties:
|
||||
* - state : anti-CSRF; verified against signed httpOnly cookie
|
||||
* - nonce : anti-replay; embedded in ID token by Google and checked here
|
||||
* - PKCE S256 : prevents auth code interception even if code leaks
|
||||
*
|
||||
* Author: Kenneth Obsequio (@lash0000)
|
||||
* Date Created: Jun. 21, 2026
|
||||
***********************************************************************************************************************************************************************/
|
||||
'use strict';
|
||||
|
||||
const crypto = require('crypto');
|
||||
const { OAuth2Client } = require('google-auth-library');
|
||||
const axios = require('axios');
|
||||
|
||||
const GOOGLE_AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth';
|
||||
const GOOGLE_TOKEN_URL = 'https://oauth2.googleapis.com/token';
|
||||
|
||||
// Lazily initialised so the module can be required before env is loaded.
|
||||
let _client;
|
||||
const getClient = () => {
|
||||
if (!_client) _client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
|
||||
return _client;
|
||||
};
|
||||
|
||||
// ── Generators ─────────────────────────────────────────────────────────────────
|
||||
const generateState = () => crypto.randomBytes(32).toString('hex');
|
||||
const generateNonce = () => crypto.randomBytes(32).toString('hex');
|
||||
|
||||
const generatePKCE = () => {
|
||||
const codeVerifier = crypto.randomBytes(32).toString('base64url');
|
||||
const codeChallenge = crypto.createHash('sha256').update(codeVerifier).digest('base64url');
|
||||
return { codeVerifier, codeChallenge };
|
||||
};
|
||||
|
||||
// ── Auth URL ───────────────────────────────────────────────────────────────────
|
||||
const buildAuthUrl = (state, nonce, codeChallenge) => {
|
||||
const params = new URLSearchParams({
|
||||
client_id: process.env.GOOGLE_CLIENT_ID,
|
||||
redirect_uri: process.env.GOOGLE_CALLBACK_URL,
|
||||
response_type: 'code',
|
||||
scope: 'openid email profile',
|
||||
state,
|
||||
nonce,
|
||||
code_challenge: codeChallenge,
|
||||
code_challenge_method: 'S256',
|
||||
access_type: 'offline',
|
||||
prompt: 'select_account',
|
||||
});
|
||||
return `${GOOGLE_AUTH_URL}?${params.toString()}`;
|
||||
};
|
||||
|
||||
// ── Code exchange ──────────────────────────────────────────────────────────────
|
||||
const exchangeCode = async (code, codeVerifier) => {
|
||||
const { data } = await axios.post(
|
||||
GOOGLE_TOKEN_URL,
|
||||
new URLSearchParams({
|
||||
code,
|
||||
client_id: process.env.GOOGLE_CLIENT_ID,
|
||||
client_secret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
redirect_uri: process.env.GOOGLE_CALLBACK_URL,
|
||||
grant_type: 'authorization_code',
|
||||
code_verifier: codeVerifier,
|
||||
}).toString(),
|
||||
{ headers: { 'Content-Type': 'application/x-www-form-urlencoded' } },
|
||||
);
|
||||
return data; // { access_token, id_token, expires_in, token_type, ... }
|
||||
};
|
||||
|
||||
// ── ID token verification ──────────────────────────────────────────────────────
|
||||
const verifyIdToken = async (idToken, expectedNonce) => {
|
||||
const ticket = await getClient().verifyIdToken({
|
||||
idToken,
|
||||
audience: process.env.GOOGLE_CLIENT_ID,
|
||||
});
|
||||
const payload = ticket.getPayload();
|
||||
|
||||
// Verify nonce to prevent token replay attacks.
|
||||
if (payload.nonce !== expectedNonce) throw new Error('Nonce mismatch');
|
||||
|
||||
return payload; // { sub, email, given_name, family_name, name, picture, ... }
|
||||
};
|
||||
|
||||
module.exports = { generateState, generateNonce, generatePKCE, buildAuthUrl, exchangeCode, verifyIdToken };
|
||||
Reference in New Issue
Block a user