0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-16 21:56:25 -05:00

wip login web

This commit is contained in:
Damien Fernandes 2024-02-22 16:28:11 +01:00
parent fbcc36fbb6
commit fd0c6354a6
No known key found for this signature in database
GPG key ID: 0E48A5C68744C7BA
2 changed files with 117 additions and 0 deletions

View file

@ -1,3 +1,4 @@
import bodyParser from 'body-parser';
import express, { Router } from 'express'; import express, { Router } from 'express';
import { Auth } from '@verdaccio/auth'; import { Auth } from '@verdaccio/auth';
@ -18,6 +19,7 @@ import publish from './publish';
import search from './search'; import search from './search';
import stars from './stars'; import stars from './stars';
import user from './user'; import user from './user';
import login from './v1/login';
import profile from './v1/profile'; import profile from './v1/profile';
import v1Search from './v1/search'; import v1Search from './v1/search';
import token from './v1/token'; import token from './v1/token';
@ -44,6 +46,8 @@ export default function (config: Config, auth: Auth, storage: Storage): Router {
app.use(auth.apiJWTmiddleware()); app.use(auth.apiJWTmiddleware());
app.use(express.json({ strict: false, limit: config.max_body_size || '10mb' })); app.use(express.json({ strict: false, limit: config.max_body_size || '10mb' }));
app.use(antiLoop(config)); app.use(antiLoop(config));
// TODO : to be removed once we have a react login route
app.use(bodyParser.urlencoded({ extended: true }));
// encode / in a scoped package name to be matched as a single parameter in routes // encode / in a scoped package name to be matched as a single parameter in routes
app.use(encodeScopePackage); app.use(encodeScopePackage);
// for "npm whoami" // for "npm whoami"
@ -54,6 +58,7 @@ export default function (config: Config, auth: Auth, storage: Storage): Router {
distTags(app, auth, storage); distTags(app, auth, storage);
publish(app, auth, storage); publish(app, auth, storage);
ping(app); ping(app);
login(app, auth, storage, config);
stars(app, storage); stars(app, storage);
v1Search(app, auth, storage); v1Search(app, auth, storage);
token(app, auth, storage, config); token(app, auth, storage, config);

View file

@ -0,0 +1,112 @@
import { randomUUID } from 'crypto';
import { Request, Response, Router } from 'express';
import { isUUID } from 'validator';
import { Auth, getApiToken } from '@verdaccio/auth';
import { createRemoteUser } from '@verdaccio/config';
import { HEADERS, HTTP_STATUS, errorUtils } from '@verdaccio/core';
import { Storage } from '@verdaccio/store';
import { Config, RemoteUser } from '@verdaccio/types';
import { getAuthenticatedMessage } from '@verdaccio/utils';
import { $NextFunctionVer } from '../../types/custom';
function addNpmLoginApi(route: Router, auth: Auth, storage: Storage, config: Config): void {
route.post('/-/v1/login', function (req: Request, res: Response): void {
const sessionId = randomUUID();
res.status(200).json({
loginUrl: 'http://localhost:8000/-/v1/login/cli/' + sessionId,
doneUrl: 'http://localhost:8000/-/v1/done?sessionId=' + sessionId,
});
});
route.get('/-/v1/done', function (req: Request, res: Response): void {
if (!req.query.sessionId) {
res.status(400).json({ error: 'missing session id' });
return;
}
const sessionId = req.query.sessionId.toString();
if (!isUUID(sessionId, 4)) {
res.status(400).json({ error: 'invalid session id' });
return;
}
// const tokens = storage.readTokens();
// TODO : check if the token have been created in storage with the sessionId as key
const ready = false;
if (!ready) {
// TODO : variable retry-after should be configurable in the config
res.header('retry-after', '5');
res.status(202).json({});
return;
}
res.status(200).json({ token: 'sample_token_not working' });
});
route.get('/-/v1/login/cli/:sessionId', function (req: Request, res: Response): void {
// TODO : This should be a webUI route but i dunno how to do it with React
res.send(`
<form action="/-/v1/login/cli/${req.params.sessionId}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required><br><br>
<input type="submit" value="Login">
</form>
`);
});
route.post(
'/-/v1/login/cli/:sessionId',
async function (req: Request, res: Response, next: $NextFunctionVer): Promise<void> {
const { username, password } = req.body;
if (!req.params.sessionId) {
res.status(400).json({ error: 'missing session id' });
return;
}
const sessionId = req.params.sessionId.toString();
if (!isUUID(sessionId, 4)) {
res.status(400).json({ error: 'invalid session id' });
return;
}
// Perform authentication logic here
auth.authenticate(
username,
password,
async function callbackAuthenticate(err, user): Promise<void> {
if (err) {
return next(errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, err.message));
}
const restoredRemoteUser: RemoteUser = createRemoteUser(username, user?.groups || []);
const token = await getApiToken(auth, config, restoredRemoteUser, password);
if (!token) {
return next(errorUtils.getUnauthorized());
}
res.status(HTTP_STATUS.CREATED);
res.set(HEADERS.CACHE_CONTROL, 'no-cache, no-store');
const message = getAuthenticatedMessage(restoredRemoteUser.name ?? '');
// TODO : save the token in storage with the sessionId as key
await storage.saveToken({
user: restoredRemoteUser.name as string,
token: token,
key: sessionId,
readonly: false,
created: '',
});
return next({
ok: message,
});
}
);
}
);
}
export default addNpmLoginApi;