Implementing an Authenticated File Upload Feature Using Amazon Cognito
Introduction
This is Katsumata.
This time, I built a file upload system with authentication functionality using Amazon Cognito, while learning how to set up user pools and ID pools. I implemented all the necessary features, from user registration and login functions to secure file uploads to S3.
What I Did
Prerequisites
- Created an S3 bucket for uploads
To enable direct file uploads to the S3 bucket, I configured the CORS settings as follows:
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"GET",
"PUT",
"POST",
"DELETE",
"HEAD"
],
"AllowedOrigins": [
"*"
],
"ExposeHeaders": [
"ETag"
]
}
]
Creating a User Pool
-
Create a user pool to manage user authentication. Select "Create a user pool" from the Cognito console.
-
Configure and create the user pool as follows:
App client type: Single-page application (SPA)
Sign-in identifier options: Email address, Username
Required attributes: email, name
Callback URL: http://localhost:8000
### Creating ID Pool
-
From the Cognito console, select "Create new identity pool".
-
Under "Configure authentication providers" in "User access", I selected both "Authenticated access" and "Guest access". For "Authenticated identity sources", I selected "Amazon Cognito user pool".
- I created new IAM roles for both "Authenticated access" and "Guest access". The IAM role for "Authenticated access" will be edited later.
Authenticated role: Cognito_MyFileUploadAuth_Role
Unauthenticated role: Cognito_MyFileUploadUnauth_Role
- Configure the connection settings with the user pool in ID provider connection. Select the user pool created earlier and the app client ID linked to the user pool.
- To grant S3 operation permissions to the "Authenticated access" IAM role, attach the following policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::your-bucket-name/*"
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::your-bucket-name"
}
]
}
```### Creating HTML Files and Verifying Operation
1. We will create an authenticated application that can upload files to S3. Below is the HTML file used for this purpose. I created it with significant help from AI. Replace userPoolId, clientId, identityPoolId, and bucketName with values from your own environment.
:::details HTML Example
```html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cognito File Upload App</title>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.1400.0.min.js"></script>
<script src="https://unpkg.com/amazon-cognito-identity-js@6.3.7/dist/amazon-cognito-identity.min.js"></script>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.container { margin: 20px 0; padding: 20px; border: 1px solid #ddd; border-radius: 5px; }
.hidden { display: none; }
button { padding: 10px 20px; margin: 5px; cursor: pointer; background-color: #007bff; color: white; border: none; border-radius: 3px; }
button:hover { background-color: #0056b3; }
input { padding: 8px; margin: 5px; width: 200px; border: 1px solid #ddd; border-radius: 3px; }
.file-list { margin-top: 20px; }
.file-item { padding: 10px; border-bottom: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; }
.debug { background-color: #f8f9fa; padding: 10px; margin: 10px 0; border-radius: 5px; font-family: monospace; max-height: 150px; overflow-y: auto; border: 1px solid #dee2e6; }
.error { color: #dc3545; }
.success { color: #28a745; }
.warning { color: #ffc107; }
.status { margin: 10px 0; padding: 10px; border-radius: 3px; }
.status.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.status.error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
</style>
</head>
<body>
<h1>🚀 Cognito File Upload App</h1>
<!-- Debug Information (collapsible) -->
<div class="container">
<h3 onclick="toggleDebug()" style="cursor: pointer;">🔧 Debug Information (Click to show/hide)</h3>
<div id="debugInfo" class="debug hidden">
<div id="debugLog"></div>
</div>
</div>
<!-- Pre-login Screen -->
<div id="loginSection" class="container">
<h2>Login</h2>
<div>
<input type="text" id="username" placeholder="Username"><br>
<input type="password" id="password" placeholder="Password"><br>
<button onclick="login()">Login</button>
<button onclick="showSignup()">Register</button>
</div>
<div id="loginStatus"></div>
</div>
<!-- Registration Screen -->
<div id="signupSection" class="container hidden">
<h2>Register</h2>
<div>
<input type="text" id="signupUsername" placeholder="Username"><br>
<input type="email" id="signupEmail" placeholder="Email address"><br>
<input type="password" id="signupPassword" placeholder="Password"><br>
<input type="text" id="signupName" placeholder="Name"><br>
<button onclick="signup()">Register</button>
<button onclick="showLogin()">Return to Login</button>
</div>
<div id="signupStatus"></div>
</div>
<!-- Confirmation Code Screen -->
<div id="confirmSection" class="container hidden">
<h2>Enter Confirmation Code</h2>
<p>Please enter the confirmation code sent to your email</p>
<div>
<input type="text" id="confirmCode" placeholder="Confirmation Code"><br>
<button onclick="confirmSignup()">Confirm</button>
</div>
<div id="confirmStatus"></div>
</div>
<!-- Post-login Screen -->
<div id="mainSection" class="container hidden">
<h2>File Upload</h2>
<div id="userInfo"></div>
<!-- AWS Connection Status Display -->
<div id="awsStatus" class="status"></div>
<!-- File Upload -->
<div style="margin: 20px 0; padding: 15px; border: 2px dashed #007bff; border-radius: 5px;">
<h3>📁 Upload Files</h3>
<input type="file" id="fileInput" multiple accept="*/*">
<br><br>
<button onclick="uploadFiles()">📤 Upload</button>
<button onclick="listFiles()">🔄 Update File List</button>
<button onclick="logout()">🚪 Logout</button>
</div>
<div id="uploadStatus"></div>
<!-- File List -->
<div class="file-list">
<h3>📋 Uploaded Files</h3>
<div id="fileList"></div>
</div>
</div>
<script>
// Configuration values
const CONFIG = {
userPoolId: 'ap-northeast-1_XXXXXXXXX',
clientId: 'XXXXXXXXXXXXXXXXXXXXXXXXXX',
identityPoolId: 'ap-northeast-1:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
bucketName: 'your-bucket-name-here',
region: 'ap-northeast-1'
};
// Debug log function
function debugLog(message, type = 'info') {
const debugDiv = document.getElementById('debugLog');
const timestamp = new Date().toLocaleTimeString();
const className = type === 'error' ? 'error' : type === 'success' ? 'success' : type === 'warning' ? 'warning' : '';
debugDiv.innerHTML += `<div class="${className}">[${timestamp}] ${message}</div>`;
debugDiv.scrollTop = debugDiv.scrollHeight;
console.log(`[${timestamp}] ${message}`);
}
function toggleDebug() {
const debugInfo = document.getElementById('debugInfo');
debugInfo.classList.toggle('hidden');
}
// Initialization
debugLog('Application started');
const poolData = {
UserPoolId: CONFIG.userPoolId,
ClientId: CONFIG.clientId
};
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
let currentUser = null;
let s3 = null;
// Screen display control
function showLogin() {
document.getElementById('loginSection').classList.remove('hidden');
document.getElementById('signupSection').classList.add('hidden');
document.getElementById('confirmSection').classList.add('hidden');
document.getElementById('mainSection').classList.add('hidden');
}
function showSignup() {
document.getElementById('loginSection').classList.add('hidden');
document.getElementById('signupSection').classList.remove('hidden');
}
function showMain() {
document.getElementById('loginSection').classList.add('hidden');
document.getElementById('signupSection').classList.add('hidden');
document.getElementById('confirmSection').classList.add('hidden');
document.getElementById('mainSection').classList.remove('hidden');
}
// Registration
function signup() {
const username = document.getElementById('signupUsername').value;
const email = document.getElementById('signupEmail').value;
const password = document.getElementById('signupPassword').value;
const name = document.getElementById('signupName').value;
const statusDiv = document.getElementById('signupStatus');
if (!username || !email || !password || !name) {
statusDiv.innerHTML = '<div class="status error">Please fill in all fields</div>';
return;
}
const attributeList = [
new AmazonCognitoIdentity.CognitoUserAttribute({
Name: 'email',
Value: email
}),
new AmazonCognitoIdentity.CognitoUserAttribute({
Name: 'name',
Value: name
})
];
statusDiv.innerHTML = '<div class="status">Registering...</div>';
userPool.signUp(username, password, attributeList, null, function(err, result) {
if (err) {
statusDiv.innerHTML = `<div class="status error">Registration error: ${err.message}</div>`;
debugLog('Registration error: ' + err.message, 'error');
return;
}
currentUser = result.user;
statusDiv.innerHTML = '<div class="status success">Confirmation code sent to your email</div>';
debugLog('Registration successful', 'success');
document.getElementById('signupSection').classList.add('hidden');
document.getElementById('confirmSection').classList.remove('hidden');
});
}
// Confirmation code entry
function confirmSignup() {
const confirmCode = document.getElementById('confirmCode').value;
const statusDiv = document.getElementById('confirmStatus');
if (!confirmCode) {
statusDiv.innerHTML = '<div class="status error">Please enter confirmation code</div>';
return;
}
statusDiv.innerHTML = '<div class="status">Confirming...</div>';
currentUser.confirmRegistration(confirmCode, true, function(err, result) {
if (err) {
statusDiv.innerHTML = `<div class="status error">Confirmation error: ${err.message}</div>`;
debugLog('Confirmation error: ' + err.message, 'error');
return;
}
statusDiv.innerHTML = '<div class="status success">Registration complete! Please login</div>';
debugLog('Account confirmation successful', 'success');
setTimeout(showLogin, 2000);
});
}
// Login
function login() {
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
const statusDiv = document.getElementById('loginStatus');
if (!username || !password) {
statusDiv.innerHTML = '<div class="status error">Please enter username and password</div>';
return;
}
statusDiv.innerHTML = '<div class="status">Logging in...</div>';
debugLog(`Login attempt: ${username}`);
const authenticationData = {
Username: username,
Password: password
};
const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
const userData = {
Username: username,
Pool: userPool
};
const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
statusDiv.innerHTML = '<div class="status success">Login successful!</div>';
debugLog('Login successful', 'success');
currentUser = cognitoUser;
showMain();
// Display user information
cognitoUser.getUserAttributes(function(err, attributes) {
if (!err) {
const name = attributes.find(attr => attr.Name === 'name')?.Value || username;
document.getElementById('userInfo').innerHTML = `<h3>👋 Welcome, ${name}!</h3>`;
}
});
// Initialize AWS
initializeAWS(result);
},
onFailure: function(err) {
statusDiv.innerHTML = `<div class="status error">Login error: ${err.message}</div>`;
debugLog('Login error: ' + err.message, 'error');
}
});
}
// AWS SDK initialization
function initializeAWS(result) {
debugLog('AWS initialization started');
const awsStatusDiv = document.getElementById('awsStatus');
awsStatusDiv.innerHTML = '<div class="status">Connecting to AWS...</div>';
try {
AWS.config.region = CONFIG.region;
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: CONFIG.identityPoolId,
Logins: {
[`cognito-idp.${CONFIG.region}.amazonaws.com/${CONFIG.userPoolId}`]: result.getIdToken().getJwtToken()
}
});
AWS.config.credentials.refresh(function(err) {
if (err) {
awsStatusDiv.innerHTML = `<div class="status error">❌ AWS connection error: ${err.message}</div>`;
debugLog('AWS authentication error: ' + err.message, 'error');
} else {
awsStatusDiv.innerHTML = '<div class="status success">✅ AWS connection successful - S3 available</div>';
debugLog('AWS authentication successful', 'success');
s3 = new AWS.S3();
listFiles();
}
});
} catch (error) {
awsStatusDiv.innerHTML = `<div class="status error">❌ AWS initialization error: ${error.message}</div>`;
debugLog('AWS initialization exception: ' + error.message, 'error');
}
}
// File upload
function uploadFiles() {
if (!s3) {
alert('❌ S3 is not initialized. Please check AWS connection.');
return;
}
const fileInput = document.getElementById('fileInput');
const files = fileInput.files;
const statusDiv = document.getElementById('uploadStatus');
if (files.length === 0) {
alert('📁 Please select files');
return;
}
debugLog(`File upload started: ${files.length} files`);
statusDiv.innerHTML = `<div class="status">📤 Uploading ${files.length} files...</div>`;
let uploadCount = 0;
let successCount = 0;
const totalFiles = files.length;
for (let i = 0; i < files.length; i++) {
const file = files[i];
const key = `uploads/${currentUser.username}/${Date.now()}_${file.name}`;
const params = {
Bucket: CONFIG.bucketName,
Key: key,
Body: file,
ContentType: file.type
};
debugLog(`Upload started: ${file.name}`);
s3.upload(params, function(err, data) {
uploadCount++;
if (err) {
debugLog(`Upload error: ${file.name} - ${err.message}`, 'error');
} else {
successCount++;
debugLog(`Upload successful: ${file.name}`, 'success');
}
// Check if all files are processed
if (uploadCount === totalFiles) {
if (successCount === totalFiles) {
statusDiv.innerHTML = `<div class="status success">✅ ${totalFiles} files uploaded successfully!</div>`;
} else {
statusDiv.innerHTML = `<div class="status error">⚠️ ${successCount}/${totalFiles} files uploaded</div>`;
}
fileInput.value = '';
setTimeout(() => {
listFiles();
}, 1000);
}
});
}
}
// Get file list
function listFiles() {
if (!s3 || !currentUser) {
document.getElementById('fileList').innerHTML = '<div class="status error">S3 is not initialized</div>';
return;
}
debugLog('Getting file list');
const params = {
Bucket: CONFIG.bucketName,
Prefix: `uploads/${currentUser.username}/`
};
s3.listObjects(params, function(err, data) {
const fileListDiv = document.getElementById('fileList');
if (err) {
fileListDiv.innerHTML = `<div class="status error">Failed to get file list: ${err.message}</div>`;
debugLog('File list retrieval error: ' + err.message, 'error');
return;
}
debugLog(`File list retrieved successfully: ${data.Contents.length} files`, 'success');
if (data.Contents.length === 0) {
fileListDiv.innerHTML = '<div class="status">📂 No uploaded files</div>';
return;
}
let html = '';
data.Contents.forEach(function(file) {
const fileName = file.Key.split('/').pop();
const fileSize = (file.Size / 1024).toFixed(2);
const uploadDate = file.LastModified.toLocaleDateString();
html += `
<div class="file-item">
<div>
<strong>📄 ${fileName}</strong><br>
<small>Size: ${fileSize} KB | Upload date: ${uploadDate}</small>
</div>
<div>
<button onclick="downloadFile('${file.Key}', '${fileName}')" style="background-color: #28a745;">⬇️ Download</button>
<button onclick="deleteFile('${file.Key}', '${fileName}')" style="background-color: #dc3545;">🗑️ Delete</button>
</div>
</div>
`;
});
fileListDiv.innerHTML = html;
});
}
// File download
function downloadFile(key, fileName) {
if (!s3) return;
debugLog(`Download started: ${fileName}`);
const params = {
Bucket: CONFIG.bucketName,
Key: key,
Expires: 300 // URL valid for 5 minutes
};
try {
const url = s3.getSignedUrl('getObject', params);
const link = document.createElement('a');
link.href = url;
link.download = fileName;
link.target = '_blank';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
debugLog(`Download successful: ${fileName}`, 'success');
} catch (error) {
debugLog(`Download error: ${fileName} - ${error.message}`, 'error');
alert(`Download error: ${error.message}`);
}
}
// File deletion
function deleteFile(key, fileName) {
if (!s3) return;
if (!confirm(`🗑️ Delete "${fileName}"?\n\nThis action cannot be undone.`)) return;
debugLog(`Deletion started: ${fileName}`);
const params = {
Bucket: CONFIG.bucketName,
Key: key
};
s3.deleteObject(params, function(err, data) {
if (err) {
debugLog(`Deletion error: ${fileName} - ${err.message}`, 'error');
alert(`Deletion error: ${err.message}`);
} else {
debugLog(`Deletion successful: ${fileName}`, 'success');
alert(`✅ Deleted "${fileName}"`);
listFiles(); // Update file list
}
});
}
// Logout
function logout() {
if (currentUser) {
currentUser.signOut();
currentUser = null;
s3 = null;
debugLog('Logout complete');
}
// Clear screen
document.getElementById('userInfo').innerHTML = '';
document.getElementById('uploadStatus').innerHTML = '';
document.getElementById('fileList').innerHTML = '';
document.getElementById('awsStatus').innerHTML = '';
showLogin();
}
// Initialization complete
debugLog('Application initialization complete', 'success');
</script>
</body>
</html>
:::2. Open the created HTML file in a browser and check its operation.
- After logging in, I uploaded an arbitrary file and confirmed that the upload completion screen and the object exist in the S3 bucket.
Conclusion
Thank you for reading.
In this article, I explained how to build a file upload system with authentication features using Amazon Cognito.
I feel that I was able to learn about how Cognito is used through the building of user pools and ID pools on the console side, and the implementation of a series of flows including user registration, confirmation code verification, and login on the application side.
About Annotation Inc.
Annotation Inc. is a company within the Classmethod Group that specializes in operations. Our specialized teams in support, operations, development maintenance, information systems, and back office utilize the latest IT technologies, high technical skills, and accumulated know-how to solve our customers' challenges. We are recruiting members for various positions. If you are interested in our culture, systems, and work styles that together realize "Operational Excellence" and "Working and living true to yourself," please visit the Annotation Inc. Recruitment Site.