Created
April 17, 2020 10:41
-
-
Save iamyellow/7051e1bcd5792f22169883223141dc28 to your computer and use it in GitHub Desktop.
protecting with a password NestJs Swagger docs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<html> | |
<head> | |
<title></title> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<meta charset="utf-8" /> | |
<!-- thanks to https://github.com/Mehedi61/Login-Signup-form/ --> | |
<style> | |
@import url(https://fonts.googleapis.com/css?family=Roboto:300); | |
.body { | |
background-image: url('https://ordinaryfaith.net/wp-content/uploads/2016/03/Gray-plain-website-background.jpg'); | |
background-repeat: no-repeat; | |
background-size: cover; | |
} | |
.login-page { | |
width: 360px; | |
padding: 8% 0 0; | |
margin: auto; | |
} | |
.form { | |
position: relative; | |
z-index: 1; | |
background: #48c9b0; | |
max-width: 360px; | |
margin: 0 auto 100px; | |
padding: 45px; | |
text-align: center; | |
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.2), | |
0 5px 5px 0 rgba(0, 0, 0, 0.24); | |
} | |
.form input { | |
font-family: FontAwesome, 'Roboto', sans-serif; | |
outline: 0; | |
background: #f2f2f2; | |
width: 100%; | |
border: 0; | |
margin: 0 0 15px; | |
padding: 15px; | |
box-sizing: border-box; | |
font-size: 14px; | |
} | |
.form button { | |
font-family: 'Titillium Web', sans-serif; | |
font-size: 14px; | |
font-weight: bold; | |
letter-spacing: 0.1em; | |
outline: 0; | |
background: #17a589; | |
width: 100%; | |
border: 0; | |
margin: 0px 0px 8px; | |
padding: 15px; | |
color: #ffffff; | |
-webkit-transition: all 0.3 ease; | |
transition: all 0.3 ease; | |
cursor: pointer; | |
} | |
.form button:hover, | |
.form button:active, | |
.form button:focus { | |
background: #148f77; | |
} | |
.form .message { | |
margin: 6px 6px; | |
color: #808080; | |
font-size: 11px; | |
text-align: center; | |
font-weight: bold; | |
font-style: normal; | |
} | |
.form .message a { | |
color: #ffffff; | |
text-decoration: none; | |
font-size: 13px; | |
} | |
.form .register-form { | |
display: none; | |
} | |
.container { | |
position: relative; | |
z-index: 1; | |
max-width: 300px; | |
margin: 0 auto; | |
} | |
.container:before, | |
.container:after { | |
content: ''; | |
display: block; | |
clear: both; | |
} | |
.container .info { | |
margin: 50px auto; | |
text-align: center; | |
} | |
.container .info h1 { | |
margin: 0 0 15px; | |
padding: 0; | |
font-size: 36px; | |
font-weight: 300; | |
color: #1a1a1a; | |
} | |
.container .info span { | |
color: #4d4d4d; | |
font-size: 12px; | |
} | |
.container .info span a { | |
color: #000000; | |
text-decoration: none; | |
} | |
.container .info span .fa { | |
color: #ef3b3a; | |
} | |
body { | |
background: #76b852; /* fallback for old browsers */ | |
background: -webkit-linear-gradient(right, #76b852, #8dc26f); | |
background: -moz-linear-gradient(right, #76b852, #8dc26f); | |
background: -o-linear-gradient(right, #76b852, #8dc26f); | |
background: linear-gradient(to left, #76b852, #8dc26f); | |
font-family: 'Roboto', sans-serif; | |
-webkit-font-smoothing: antialiased; | |
-moz-osx-font-smoothing: grayscale; | |
} | |
</style> | |
<link | |
rel="stylesheet" | |
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" | |
/> | |
<link | |
href="https://fonts.googleapis.com/css?family=Titillium+Web:400,300,600" | |
rel="stylesheet" | |
type="text/css" | |
/> | |
</head> | |
<body class="body"> | |
<div class="login-page"> | |
<div class="form"> | |
<form method="post" enctype="application/json"> | |
<input | |
name="you_know" | |
type="password" | |
placeholder=" you know" | |
/> | |
<button type="submit">go</button> | |
<p class="message"></p> | |
</form> | |
</div> | |
</div> | |
</body> | |
</html> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { ValidationPipe } from '@nestjs/common' | |
import { NestFactory } from '@nestjs/core' | |
import { | |
FastifyAdapter, | |
NestFastifyApplication | |
} from '@nestjs/platform-fastify' | |
import { FastifyInstance } from 'fastify' | |
import setUpSwagger from './setUpSwagger' | |
import { AppModule } from './app.module' | |
async function bootstrap() { | |
const adapter = new FastifyAdapter() | |
const server: FastifyInstance = adapter.getInstance() | |
const app = await NestFactory.create<NestFastifyApplication>( | |
AppModule, | |
adapter | |
) | |
app.useGlobalPipes(new ValidationPipe()) | |
setUpSwagger(app, server) | |
await app.listen(3000, '0.0.0.0') | |
} | |
bootstrap() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { NestFastifyApplication } from '@nestjs/platform-fastify' | |
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' | |
import Cookies from 'cookies' | |
import { FastifyInstance } from 'fastify' | |
import { createReadStream } from 'fs' | |
import jwt from 'jsonwebtoken' | |
const DOC_ENDPOINT = 'doc' | |
const DOC_LOGIN_ENDPOINT = 'doc-login' | |
const DOC_LOGIN_HTML_PATH = `${process.cwd()}/doc-login.html` | |
const COOKIE_NAME = 'ms::api-front-doc' | |
export default function setUpSwagger( | |
app: NestFastifyApplication, | |
server: FastifyInstance | |
) { | |
server.after(() => { | |
server.addHook('onRoute', (opts) => { | |
const url = opts.url | |
if ( | |
url !== `/${DOC_LOGIN_ENDPOINT}` && | |
url.indexOf(`/${DOC_ENDPOINT}`) === 0 | |
) { | |
opts.onRequest = (request, reply, next) => { | |
const keys = [process.env.DOC_LOGIN_KEY as string] | |
const cookies = new Cookies(request.req, reply.res, { | |
keys: keys | |
}) | |
const cookie = cookies.get(COOKIE_NAME, { signed: true }) | |
if (cookie) { | |
try { | |
jwt.verify(cookie, keys[0]) | |
next() | |
return | |
} catch {} | |
} | |
reply.redirect(`/${DOC_LOGIN_ENDPOINT}`) | |
} | |
} | |
}) | |
// login routes | |
server.route({ | |
method: 'GET', | |
url: '/doc-login', | |
handler: (_request, reply) => { | |
reply | |
.header('Content-Type', 'text/html; charset=UTF-8') | |
.send(createReadStream(DOC_LOGIN_HTML_PATH, 'utf8')) | |
} | |
}) | |
server.route({ | |
method: 'POST', | |
url: '/doc-login', | |
handler: (request, reply) => { | |
const keys = [process.env.DOC_LOGIN_KEY as string] | |
const pw = process.env.DOC_LOGIN_PW as string | |
const you_know: string = request.body.you_know | |
if (you_know !== pw) { | |
reply.redirect(`/${DOC_LOGIN_ENDPOINT}`) | |
return | |
} | |
const token = jwt.sign({ id: new Date().getTime() }, keys[0]) | |
const cookies = new Cookies(request.req, reply.res, { | |
keys: keys | |
}) | |
cookies.set(COOKIE_NAME, token, { signed: true }) | |
reply.redirect(`/${DOC_ENDPOINT}`) | |
} | |
}) | |
}) | |
const options = new DocumentBuilder() | |
.setTitle('api-front docs') | |
.setVersion('1.0') | |
.build() | |
const document = SwaggerModule.createDocument(app, options) | |
SwaggerModule.setup(DOC_ENDPOINT, app, document) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I realize this thread is 3 years old, but I struggled to get this code working (as some things have breaking changes, such as reply.res no longer exists and is apparently reply.raw), and the redirect code only fires on assets (JS and CSS) but not on the top-level route. Ultimately, I decided to eliminate the cookie and the HTML webpage and just throw Basic Auth HTTP headers. Hopefully this code helps others:
import { INestApplication } from '@nestjs/common';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { FastifyInstance } from 'fastify';
export function setupSwagger(app: INestApplication, fastify: FastifyInstance) {
const docPath = 'api-docs';
const docLoginUsername = 'admin';
const docLoginPassword = 'abc123';
fastify.after(() => {
fastify.addHook('onRequest', (request, reply, next) => {
if (request.url.indexOf('/' + docPath) === 0) {
try {
const base64AuthString = request.headers['authorization'].split(' ')[1];
const authString = Buffer.from(base64AuthString, 'base64').toString('utf8');
const username = authString.split(':')[0];
const password = authString.split(':')[1];
});
const config = new DocumentBuilder()
.setTitle('The API')
.setDescription('The API that controls all backend functionality')
.setVersion('1.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup(docPath, app, document);
}