Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions ProcessMaker/Http/Controllers/Auth/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Validation\ValidationException;
use Laravel\Passport\ApiTokenCookieFactory;
use Laravel\Passport\Passport;
use ProcessMaker\Events\Logout;
use ProcessMaker\Http\Controllers\Controller;
Expand Down Expand Up @@ -243,9 +244,29 @@ public function username()
return 'username';
}

public function keepAlive()
public function keepAlive(Request $request)
{
return response('', 204);
// Touch the Laravel session to extend its server-side expiration.
// Return the session CSRF token and refresh Passport's API cookie.
$token = '';
try {
$request->session()->put('_pm_keep_alive', time());
$token = (string) $request->session()->token();
} catch (\Throwable $e) {
// If session is unavailable, return an empty token and let the frontend handle auth failure.
}

$response = response()->json(['token' => $token]);

if (Auth::check() && $token !== '') {
// Passport's CreateFreshApiToken middleware only refreshes this
// cookie on GET requests. keep-alive is POST, so refresh it here.
$response->withCookie(
app(ApiTokenCookieFactory::class)->make(Auth::id(), $token)
);
}

return $response;
}

protected function authenticated(Request $request, $user)
Expand Down
34 changes: 25 additions & 9 deletions resources/js/bootstrap.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import "bootstrap-vue/dist/bootstrap-vue.css";
import { BootstrapVue, BootstrapVueIcons } from "bootstrap-vue";
import * as bootstrap from "bootstrap";
import TenantAwareEcho from "./common/TenantAwareEcho";
import { initSessionSync } from "./common/sessionSync";
import Router from "vue-router";
import ScreenBuilder, { initializeScreenCache } from "@processmaker/screen-builder";
import * as VueDeepSet from "vue-deepset";
Expand All @@ -20,6 +18,14 @@ import MonacoEditor from "vue-monaco";
import Vue from "vue";
import * as vue from "vue";
import VueCookies from "vue-cookies";
import { initSessionSync } from "./common/sessionSync";
import {
applyCsrfToken,
attachCsrfRequestInterceptor,
attachSessionRenewalInterceptor,
getCsrfToken,
} from "./common/csrfToken";
import TenantAwareEcho from "./common/TenantAwareEcho";
import GlobalStore from "./globalStore";
import Pagination from "./components/common/Pagination";
import ScreenSelect from "./processes/modeler/components/inspector/ScreenSelect.vue";
Expand Down Expand Up @@ -242,19 +248,26 @@ window.ProcessMaker.i18nPromise.then(() => { translationsLoaded = true; });
*/
window.ProcessMaker.apiClient = require("axios");

window.ProcessMaker.apiClient.defaults.withCredentials = true;

window.ProcessMaker.apiClient.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";

const token = document.head.querySelector("meta[name=\"csrf-token\"]");
const isProd = document.head.querySelector("meta[name=\"is-prod\"]")?.content === "true";

window.ProcessMaker.applyCsrfToken = applyCsrfToken;
window.ProcessMaker.getCsrfToken = getCsrfToken;
// Attach CSRF interceptor before other interceptors so it runs last in the axios chain.
attachCsrfRequestInterceptor(window.ProcessMaker.apiClient);

/**
* Next we will register the CSRF Token as a common header with Axios so that
* all outgoing HTTP requests automatically have it attached. This is just
* a simple convenience so we don't have to attach every token manually.
*/

const token = document.head.querySelector("meta[name=\"csrf-token\"]");
const isProd = document.head.querySelector("meta[name=\"is-prod\"]")?.content === "true";

if (token) {
window.ProcessMaker.apiClient.defaults.headers.common["X-CSRF-TOKEN"] = token.content;
applyCsrfToken(token.content, "page-load");
} else {
console.error("CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token");
}
Expand Down Expand Up @@ -284,6 +297,8 @@ window.ProcessMaker.apiClient.interceptors.request.use((config) => {
return config;
});

attachSessionRenewalInterceptor(window.ProcessMaker.apiClient);

// Set the default API timeout
let apiTimeout = 5000;
if (window.Processmaker && window.Processmaker.apiTimeout !== undefined) {
Expand Down Expand Up @@ -353,10 +368,11 @@ if (window.Processmaker && window.Processmaker.broadcasting) {

if (userID) {
const timeoutScript = document.head.querySelector("meta[name=\"timeout-worker\"]")?.content;
const accountTimeoutLength = parseInt(eval(document.head.querySelector("meta[name=\"timeout-length\"]")?.content));
const warnSeconds = parseInt(document.head.querySelector("meta[name=\"timeout-warn-seconds\"]")?.content);
const timeoutEnabledMeta = document.head.querySelector("meta[name=\"timeout-enabled\"]")?.content;
const accountTimeoutLength = Number(document.head.querySelector("meta[name=\"timeout-length\"]")?.content);
const warnSeconds = Number(document.head.querySelector("meta[name=\"timeout-warn-seconds\"]")?.content);
const accountTimeoutWarnSeconds = Number.isNaN(warnSeconds) ? 0 : warnSeconds;
const accountTimeoutEnabled = document.head.querySelector("meta[name=\"timeout-enabled\"]") ? parseInt(document.head.querySelector("meta[name=\"timeout-enabled\"]")?.content) : 1;
const accountTimeoutEnabled = timeoutEnabledMeta ? Number(timeoutEnabledMeta) : 1;

const sessionSyncState = initSessionSync({
userId: userID.content,
Expand Down
Loading
Loading