@manujdhull/nestjs-pkce-oauth2
Version:
A NestJS plugin to simplify OAuth2 PKCE flow integration, including decorators, guards, and token/session management.
105 lines (104 loc) • 5.4 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OAuthController = void 0;
const common_1 = require("@nestjs/common");
const token_store_service_1 = require("../services/token-store.service");
const state_guard_1 = require("../guards/state.guard");
const axios_1 = __importDefault(require("axios"));
const oauth_initiate_decorator_1 = require("../decorators/oauth-initiate.decorator");
const oauth_callback_decorator_1 = require("../decorators/oauth-callback.decorator");
let OAuthController = class OAuthController {
constructor(tokenStore) {
this.tokenStore = tokenStore;
}
async authorize(pkceData, res) {
const clientId = process.env.OAUTH_CLIENT_ID;
const redirectUri = process.env.OAUTH_REDIRECT_URI;
const authUrlBase = process.env.OAUTH_AUTH_URL;
if (!clientId || !redirectUri || !authUrlBase) {
return res.status(500).send({ error: 'Missing OAuth environment configuration.' });
}
const scope = encodeURIComponent('openid email profile');
const responseType = 'code';
const authUrl = `${authUrlBase}?response_type=${responseType}&client_id=${clientId}&redirect_uri=${redirectUri}&code_challenge=${pkceData.codeChallenge}&code_challenge_method=${pkceData.codeChallengeMethod}&state=${pkceData.state}&scope=${scope}&access_type=offline&include_granted_scopes=true`;
return res.redirect(authUrl);
}
async callback(callbackData, codeVerifier, res) {
var _a, _b;
if (!codeVerifier) {
return res.status(400).send({ error: 'Missing code_verifier from frontend.' });
}
const tokenUrl = process.env.OAUTH_TOKEN_URL;
const redirectUri = process.env.OAUTH_REDIRECT_URI;
const clientId = process.env.OAUTH_CLIENT_ID;
if (!tokenUrl || !redirectUri || !clientId) {
return res.status(500).send({ error: 'Missing OAuth environment configuration.' });
}
try {
const tokenResponse = await axios_1.default.post(tokenUrl, new URLSearchParams({
code: callbackData.code,
grant_type: 'PKCE',
redirect_uri: redirectUri,
client_id: clientId,
code_verifier: codeVerifier,
}), {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
});
this.tokenStore.set(callbackData.state, tokenResponse.data);
return res.send({ message: 'Token received and session stored.' });
}
catch (error) {
if (axios_1.default.isAxiosError(error)) {
console.error('OAuth token exchange failed:', ((_a = error.response) === null || _a === void 0 ? void 0 : _a.data) || error.message);
return res.status(500).send({
error: 'Token exchange failed.',
details: ((_b = error.response) === null || _b === void 0 ? void 0 : _b.data) || error.message,
});
}
else {
console.error('Unknown error during OAuth callback:', error);
return res.status(500).send({
error: 'Unknown error occurred during token exchange.',
});
}
}
}
};
exports.OAuthController = OAuthController;
__decorate([
(0, common_1.Get)('authorize'),
__param(0, (0, oauth_initiate_decorator_1.OAuthInitiate)()),
__param(1, (0, common_1.Res)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, Object]),
__metadata("design:returntype", Promise)
], OAuthController.prototype, "authorize", null);
__decorate([
(0, common_1.UseGuards)(state_guard_1.StateGuard),
(0, common_1.Get)('callback'),
__param(0, (0, oauth_callback_decorator_1.OAuthCallback)()),
__param(1, (0, common_1.Query)('code_verifier')),
__param(2, (0, common_1.Res)()),
__metadata("design:type", Function),
__metadata("design:paramtypes", [Object, String, Object]),
__metadata("design:returntype", Promise)
], OAuthController.prototype, "callback", null);
exports.OAuthController = OAuthController = __decorate([
(0, common_1.Controller)('oauth'),
__metadata("design:paramtypes", [token_store_service_1.TokenStoreService])
], OAuthController);