Authenticate browser media with durable HttpOnly login cookies
Adapted cookie, origin, URL validation and secret handling from MikaelKW/nodecast-tv-plus. Protect API roles, revoke logins across restart, preserve existing accounts, and isolate transcode sessions by account. Co-authored-by: Mikael <mikaelkwessel@gmail.com>
This commit is contained in:
@ -1096,7 +1096,7 @@
|
||||
|
||||
<!-- JavaScript -->
|
||||
<script src="/js/icons.js"></script>
|
||||
<script src="/js/api.js?v=2"></script>
|
||||
<script src="/js/api.js?v=auth-2"></script>
|
||||
<script src="/js/components/VodDuration.js?v=1"></script>
|
||||
<script src="/js/components/VideoPlayer.js?v=2"></script>
|
||||
<script src="/js/components/ChannelList.js?v=3"></script>
|
||||
@ -1109,21 +1109,8 @@
|
||||
<script src="/js/pages/SeriesPage.js?v=1"></script>
|
||||
<script src="/js/pages/Settings.js?v=2"></script>
|
||||
<script src="/js/pages/WatchPage.js?v=2"></script>
|
||||
<script src="/js/app.js?v=4"></script>
|
||||
<script>
|
||||
// Check for SSO token in URL
|
||||
(function () {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const token = urlParams.get('token');
|
||||
if (token) {
|
||||
console.log('SSO Login successful, saving token...');
|
||||
localStorage.setItem('authToken', token);
|
||||
// Clean URL without reloading
|
||||
const newUrl = window.location.pathname;
|
||||
window.history.replaceState({}, document.title, newUrl);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<script src="/js/app.js?v=5"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -9,17 +9,12 @@ const API = {
|
||||
async request(method, endpoint, data = null) {
|
||||
const options = {
|
||||
method,
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
|
||||
// Add authentication token if available
|
||||
const token = localStorage.getItem('authToken');
|
||||
if (token) {
|
||||
options.headers['Authorization'] = `Bearer ${token}`;
|
||||
}
|
||||
|
||||
if (data) {
|
||||
options.body = JSON.stringify(data);
|
||||
}
|
||||
@ -40,7 +35,7 @@ const API = {
|
||||
if (response.status === 401) {
|
||||
localStorage.removeItem('authToken');
|
||||
window.location.href = '/login.html';
|
||||
return;
|
||||
throw new Error('Authentication required');
|
||||
}
|
||||
throw new Error(result.error || `Server responded with ${response.status}`);
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ class App {
|
||||
|
||||
async init() {
|
||||
// Check authentication first
|
||||
await this.checkAuth();
|
||||
if (!await this.checkAuth()) return;
|
||||
|
||||
// Mobile menu toggle
|
||||
const mobileMenuToggle = document.getElementById('mobile-menu-toggle');
|
||||
@ -167,19 +167,14 @@ class App {
|
||||
async checkAuth() {
|
||||
const token = localStorage.getItem('authToken');
|
||||
|
||||
if (!token) {
|
||||
// No token, redirect to login (replace to avoid back button issues)
|
||||
window.location.replace('/login.html');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Verify token with server
|
||||
const response = await fetch('/api/auth/me', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
if (token) {
|
||||
const migrated = await fetch('/api/auth/session', { method: 'POST',
|
||||
headers: { Authorization: `Bearer ${token}` } });
|
||||
localStorage.removeItem('authToken');
|
||||
if (!migrated.ok) throw new Error('Please sign in again');
|
||||
}
|
||||
const response = await fetch('/api/auth/me', { credentials: 'same-origin' });
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Invalid token');
|
||||
@ -197,11 +192,13 @@ class App {
|
||||
|
||||
// Add logout button to navbar
|
||||
this.addLogoutButton();
|
||||
return true;
|
||||
|
||||
} catch (err) {
|
||||
console.error('Authentication error:', err);
|
||||
localStorage.removeItem('authToken');
|
||||
window.location.replace('/login.html');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -223,15 +220,7 @@ class App {
|
||||
logoutLink.addEventListener('click', async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const token = localStorage.getItem('authToken');
|
||||
if (token) {
|
||||
await fetch('/api/auth/logout', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
}
|
||||
await fetch('/api/auth/logout', { method: 'POST', credentials: 'same-origin' });
|
||||
|
||||
localStorage.removeItem('authToken');
|
||||
window.location.replace('/login.html');
|
||||
|
||||
@ -299,7 +299,7 @@
|
||||
}
|
||||
|
||||
// Store token
|
||||
localStorage.setItem('authToken', data.token);
|
||||
localStorage.removeItem('authToken');
|
||||
|
||||
// Redirect to main app (replace history to prevent back button issues)
|
||||
window.location.replace('/');
|
||||
|
||||
Reference in New Issue
Block a user