Compare commits

...

4 Commits

Author SHA1 Message Date
Nikolai Giman 6c990aff4f chore: 1.50.2 2026-07-12 22:49:43 +02:00
Nikolai Giman 40cd8064f5 fix: allow login-by-code redemption for SSO and social logins (#83)
SSO and social OAuth callbacks complete the login by redeeming a
one-time code through POST /module/auth/login-by-code, but the endpoint
was gated behind RequireLoginMethod('magic-link'). With
AUTH_LOGIN_METHODS excluding magic-link (e.g. "sso"), the redemption
returned an empty 403 and the user silently bounced back to the login
page.

- Add RequireAnyLoginMethod and gate /login-by-code on
  magic-link OR sso OR social
- Show a toast on failed code redemption instead of only logging
  to console
2026-07-12 22:44:23 +02:00
Nikolai Giman f4a765f40b Merge pull request #82 from Gimanh/chore/ver-1-50
chore: version
2026-07-11 23:25:56 +02:00
Nikolai Giman bc08c839bc Merge pull request #81 from Gimanh/fix/72-ui-and-setup
Fix/72 UI and setup
2026-07-11 23:15:50 +02:00
6 changed files with 26 additions and 5 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "taskview-ce-api-server",
"version": "1.50.1",
"version": "1.50.2",
"scripts": {
"dev": "bun run --watch ./server.ts",
"start": "NODE_ENV=production node ./dist/taskview-server.js",
+4 -2
View File
@@ -3,7 +3,7 @@ import type { Routable } from '../../types/routable.type';
import AuthController from './AuthController';
import { IsLoggedIn } from './middlewares/is-logged-in';
import { RejectApiTokenAuth } from '../api-tokens/middlewares/RejectApiTokenAuth';
import { RequireLoginMethod, RequireSocialProvider } from './middlewares/require-login-method';
import { RequireAnyLoginMethod, RequireLoginMethod, RequireSocialProvider } from './middlewares/require-login-method';
import passport from './strategies/passport-login';
import { ExternalProviderScope } from './strategies/external-auth.types';
export default class AuthRoutes implements Routable {
@@ -23,7 +23,9 @@ export default class AuthRoutes implements Routable {
initRoutes() {
this.router.get('/login-options', this.authController.getLoginOptions);
this.router.post('/send-login-code', [RequireLoginMethod('magic-link')], this.authController.sendLoginCode);
this.router.post('/login-by-code', [RequireLoginMethod('magic-link')], this.authController.loginByCode);
// Shared one-time-code redemption: magic-link emails, SSO callbacks and social
// OAuth callbacks all complete the login through this endpoint
this.router.post('/login-by-code', [RequireAnyLoginMethod(['magic-link', 'sso', 'social'])], this.authController.loginByCode);
this.router.post('/login', [RequireLoginMethod('password')], this.authController.login);
this.router.post('/registration', this.authController.registration);
this.router.get('/confirm/email/:code/login/:login', this.authController.confirmEmail);
@@ -11,6 +11,15 @@ export const RequireLoginMethod = (method: LoginMethod) => {
};
};
export const RequireAnyLoginMethod = (methods: LoginMethod[]) => {
return (_req: Request, res: Response, next: NextFunction) => {
if (!methods.some((method) => LoginMethods.isEnabled(method))) {
return res.status(403).send();
}
return next();
};
};
export const RequireSocialProvider = (req: Request, res: Response, next: NextFunction) => {
const providerName = String(req.params.providerName || '').toLowerCase();
if (!LoginMethods.availableSocialProviders().includes(providerName)) {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "taskview-ce-monorepo",
"version": "1.50.1",
"version": "1.50.2",
"private": true,
"description": "TaskView CE monorepo containing web, API, and packages",
"workspaces": [
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "web-nuxt-ui",
"private": true,
"type": "module",
"version": "1.50.1",
"version": "1.50.2",
"scripts": {
"dev": "vite",
"build:packages": "pnpm --filter taskview-db-schemas build && pnpm --filter taskview-api build && pnpm --filter capacitor-widget-bridge build",
+10
View File
@@ -75,6 +75,11 @@ onMounted(async () => {
await loginByCode(result.code, result.email)
} catch (error) {
console.error('Failed to process tokens from URL:', error)
toast.add({
title: t('auth.error'),
description: t('auth.loginFailed'),
color: 'error',
})
}
})
@@ -92,6 +97,11 @@ App.addListener('appUrlOpen', async ({ url }) => {
await loginByCode(result.code, result.email)
} catch (error) {
console.error('Failed to process deep link tokens:', error)
toast.add({
title: t('auth.error'),
description: t('auth.loginFailed'),
color: 'error',
})
}
}