Hi,
I am a relatively new user to SimpleSAMLPHP. I have a page of download links on a web server which is acting as my IdP. I have a PHP script on another server (an AWS EC2 instance running Ubuntu) that then acts a SP. The idea is the PHP script will authenicate the user and then serve the file. The problem is, the first time the user clicks a download link the download shows as insecure in Chrome and the user then has to select 'Keep'. I'd rather not have that. Subsequent clicks on download links download without issue. i.e. after the user has authenticated.
I think that after the SAML redirect for authentication, the original request for the file is "lost" and Chrome sees this as an issue.
Any help would be appreciated.
Doug
These are the relevant bits of my PHP script and the javascript function follows.
<?php
header("Access-Control-Allow-Origin:
https://fromIdPServer");
header("Access-Control-Allow-Methods: GET, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type");
header("Access-Control-Max-Age: 86400");
require 'vendor/autoload.php';
require_once('/var/simplesamlphp/lib/_autoload.php');
use SimpleSAML\Session;
use Aws\S3\S3Client;
$auth = new SimpleSAML\Auth\Simple('xxx-sp');
try {
// Check if the user is authenticated
if (!$auth->isAuthenticated()) {
// Redirect for SAML authentication
$auth->requireAuth();
}
// After authentication, redirect back with a flag
if (!isset($_GET['authenticated'])) {
$redirectUrl = $_SERVER['REQUEST_URI'] . (strpos($_SERVER['REQUEST_URI'], '?') === false ? '?' : '&') . 'authenticated=1';
header("Location: $redirectUrl");
exit;
}
// Restore the application's session after SAML authentication
$session = Session::getSessionFromRequest();
$session->cleanup();
// Continue with your application logic, ensuring session continuity
session_start(); // Restart your application's session if needed
$_SESSION['authenticated_user'] = $auth->getAttributes();
// Process the file download request
$student_document_id = urldecode(basename($_GET['student_document_id'] ?? ''));
if (empty($student_document_id)) {
throw new Exception("Invalid or missing student_document_id");
}
// Fetch document details from API
$data = json_encode(['docid' => document_id]);
$response = getPowerQuery(
//get my data
);
if (isset($response['error']) || empty($response['record'][0]['tables'])) {
throw new Exception("Failed to retrieve document properties");
}
$documentProps = getDocumentProperties($response['record'][0]['tables']);
if (!$documentProps) {
throw new Exception("Invalid document properties");
}
// Construct the S3 key
$key = constructS3Key($documentProps);
// Initialize S3 client
$s3 = new S3Client([
'version' => 'latest',
'region' => 'myregion',
]);
// Fetch the file from S3
$result = $s3->getObject([
'Bucket' => 'mybucket',
'Key' => $key,
]);
// Generate a random file name for the download
$randomFileName = uniqid('report_', true) . '.pdf';
// Send file headers and content
header("Content-Type: " . $result['ContentType']);
header("Content-Disposition: attachment; filename=\"$randomFileName\"");
header("Content-Length: " . $result['ContentLength']);
echo $result['Body'];
exit;
} catch (Exception $e) {
// Log the error for debugging purposes
error_log("Error: " . $e->getMessage());
// Determine the type of error and message to display
$errorMessage = '';
$httpStatus = 500; // Default to Internal Server Error
if (str_contains($e->getMessage(), 'Access denied')) {
$httpStatus = 403;
$errorMessage = 'Access denied. You do not have permission to access this file.';
} elseif (str_contains($e->getMessage(), 'NoSuchKey')) {
$httpStatus = 404;
$errorMessage = 'File not found. The document you are looking for does not exist.';
} else {
$httpStatus = 500;
$errorMessage = 'An unexpected error occurred while processing your request.';
}
// Set the HTTP response status
header("HTTP/1.1 $httpStatus");
header("Access-Control-Allow-Origin:
https://myiDPServer");
// Serve a user-friendly HTML error message
echo "<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>Error</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f8f9fa;
color: #343a40;
margin: 0;
padding: 20px;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
text-align: center;
}
.error-container {
max-width: 600px;
padding: 20px;
background-color: #ffffff;
border: 1px solid #dee2e6;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.error-header {
font-size: 24px;
margin-bottom: 10px;
}
.error-message {
font-size: 18px;
margin-bottom: 20px;
}
.contact-support {
font-size: 14px;
color: #6c757d;
}
</style>
</head>
<body>
<div class='error-container'>
<div class='error-header'>An Error Occurred</div>
<div class='error-message'>$errorMessage</div>
<div class='contact-support'>
Please contact support if you believe this is a mistake.
</div>
</div>
</body>
</html>";
exit;
}
function constructS3Key($documentProps) {
//constructs the key
}
return "theKey";
}
function getAccessToken($id, $secret, $url) {
//gets a token for API access
}
function accessDocumentProperties(some parameter) {
gets the necessary info to make the S3 object key
}
function getDocumentProperties($docProperties) {
formats the object key
}