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:
parent
fbcc36fbb6
commit
fd0c6354a6
2 changed files with 117 additions and 0 deletions
|
@ -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);
|
||||||
|
|
112
packages/api/src/v1/login.ts
Normal file
112
packages/api/src/v1/login.ts
Normal 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;
|
Loading…
Reference in a new issue