feat: admin frontend - React + Vite, auth pages, user management, roles, permissions, webhooks, devices, logs
This commit is contained in:
29
frontend/admin/src/lib/auth/oauth.test.ts
Normal file
29
frontend/admin/src/lib/auth/oauth.test.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import {
|
||||
buildOAuthCallbackReturnTo,
|
||||
parseOAuthCallbackHash,
|
||||
sanitizeAuthRedirect,
|
||||
} from './oauth'
|
||||
|
||||
describe('oauth auth helpers', () => {
|
||||
it('sanitizes redirect paths to internal routes only', () => {
|
||||
expect(sanitizeAuthRedirect('/users')).toBe('/users')
|
||||
expect(sanitizeAuthRedirect('https://evil.example.com')).toBe('/dashboard')
|
||||
expect(sanitizeAuthRedirect('//evil.example.com')).toBe('/dashboard')
|
||||
expect(sanitizeAuthRedirect('users')).toBe('/dashboard')
|
||||
})
|
||||
|
||||
it('builds oauth callback return url on current origin', () => {
|
||||
expect(buildOAuthCallbackReturnTo('/users')).toBe('http://localhost:3000/login/oauth/callback?redirect=%2Fusers')
|
||||
})
|
||||
|
||||
it('parses oauth callback hash payload', () => {
|
||||
expect(parseOAuthCallbackHash('#status=success&code=abc&provider=github')).toEqual({
|
||||
status: 'success',
|
||||
code: 'abc',
|
||||
provider: 'github',
|
||||
message: '',
|
||||
})
|
||||
})
|
||||
})
|
||||
27
frontend/admin/src/lib/auth/oauth.ts
Normal file
27
frontend/admin/src/lib/auth/oauth.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
export function sanitizeAuthRedirect(target: string | null | undefined, fallback: string = '/dashboard'): string {
|
||||
const value = (target || '').trim()
|
||||
if (!value.startsWith('/') || value.startsWith('//')) {
|
||||
return fallback
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
export function buildOAuthCallbackReturnTo(redirectPath: string): string {
|
||||
const callbackUrl = new URL('/login/oauth/callback', window.location.origin)
|
||||
if (redirectPath && redirectPath !== '/dashboard') {
|
||||
callbackUrl.searchParams.set('redirect', redirectPath)
|
||||
}
|
||||
return callbackUrl.toString()
|
||||
}
|
||||
|
||||
export function parseOAuthCallbackHash(hash: string): Record<string, string> {
|
||||
const normalized = hash.startsWith('#') ? hash.slice(1) : hash
|
||||
const values = new URLSearchParams(normalized)
|
||||
|
||||
return {
|
||||
status: values.get('status') || '',
|
||||
code: values.get('code') || '',
|
||||
provider: values.get('provider') || '',
|
||||
message: values.get('message') || '',
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user