In today’s world, portfolios are powerful tools for showcasing skills, attracting clients, and fostering collaborations. However, sharing them often introduces a delicate challenge: how to balance security and usability? From recruiters to collaborators, sharing access comes with risks of unauthorized use, accidental exposure, and outdated credentials remaining active far too long.
For my hand-coded portfolio I needed a robust solution tailored to my specific needs. One that wouldn’t just protect sensitive information but also provide effortless control and flexibility in sharing. With these objectives, I built a Portfolio Access Management feature that strikes the perfect balance between security, usability, and convenience.
# High-level workflow for managing access
@app.route('/access_management', methods=['GET', 'POST'])
@login_required
def access_management():
if request.method == 'POST':
password = request.form.get('password')
expiration_date = request.form.get('expiration_date')
expiration_time = request.form.get('expiration_time')
note = request.form.get('note')
if password and expiration_date and expiration_time:
store_temporary_password(password, expiration_date, expiration_time, note)
flash('Temporary password added!', 'success')
else:
flash('All fields are required.', 'error')
return redirect(url_for('access_management'))
passwords = TemporaryPassword.query.all()
return render_template('access_management.html', passwords=passwords, now=datetime.now(timezone.utc))
Time-bound passwords for secure sharing
Imagine meeting someone at a conference or during travel. You find a shared interest, and you want to share your portfolio but only for a limited time. You want to ensure the access expires within hours or days without risking password misuse.
Generate passwords with custom expiration
With this feature, I can generate temporary passwords tailored to specific timeframes and contexts. For instance, I can create a password that expires in two hours for a quick review or set a longer duration for a recruiter. Once the password expires, it’s no longer valid, ensuring access remains tightly controlled even if it’s forwarded or misused. This feature gives me the confidence to share my portfolio without worrying about lingering vulnerabilities.
# Core logic to handle password storage and validation
def store_temporary_password(password, expiration_date, expiration_time, note):
hashed_password = generate_password_hash(password)
expiration_datetime = datetime.strptime(
f"{expiration_date} {expiration_time}", "%Y-%m-%d %H:%M"
).astimezone(timezone.utc)
temp_password = TemporaryPassword(
password_hash=hashed_password,
expiration_date=expiration_datetime.date(),
expiration_time=expiration_datetime.time(),
note=note
)
db.session.add(temp_password)
db.session.commit()
def validate_password(input_password):
now = datetime.now(timezone.utc)
valid_passwords = TemporaryPassword.query.filter(
(TemporaryPassword.expiration_date > now.date()) |
((TemporaryPassword.expiration_date == now.date()) &
(TemporaryPassword.expiration_time > now.time()))
).all()
for temp_password in valid_passwords:
if check_password_hash(temp_password.password_hash, input_password):
return True
return False
Conveniently copying passwords while maintaining security
The challenge with passwords is balancing privacy and usability. While storing passwords securely is non-negotiable, sharing them should also be seamless. The core issue? Once a password is created, it’s securely hashed, and the original plaintext can’t be retrieved even by me. This ensures maximum security but creates a new challenge: what if I need to share the password immediately after generating it?
A streamlined copy feature during creation
To address this, I integrated a copy-to-clipboard feature into the password creation process. When I type a new password, I can copy it immediately before it’s saved and hashed. This small but significant feature ensures I don’t have to worry about mistyping the password or losing it. I can share it with someone instantly or store it securely for future use. It’s a practical solution to an inevitable limitation of hashing, ensuring that security and usability remain aligned.
<!--Securely copy a newly created password-->
<div class="password-entry">
<input type="text" id="password-creation" value="{{ password }}" readonly>
<button id="copy-password" title="Copy to clipboard">Copy</button>
</div>
<script nonce="{{ g.nonce }}">
document.getElementById('copy-password').addEventListener('click', () => {
const passwordInput = document.getElementById('password-creation');
navigator.clipboard.writeText(passwordInput.value)
.then(() => alert('Password copied!'))
.catch(err => console.error('Error copying password:', err));
});
</script>
Efficiently managing active and expired passwords
As passwords accumulate, distinguishing between active and expired ones can become a challenge. Without clear indicators, there’s a risk of accidentally sharing expired credentials or struggling to keep track of access points.
Visual cues for expiration management
Expired passwords are flagged with a red background in the password table, providing instant clarity. This visual distinction ensures I can quickly differentiate between valid and invalid passwords, making the entire management process intuitive and error-free.
Clear feedback for critical actions
When performing key actions like creating or deleting passwords, instant confirmation is crucial. Without clear feedback, there’s uncertainty about whether the action was successful, leading to potential mistakes or unnecessary repetition.
Pre-built toast notifications
Leveraging the pre-built toast notification system in my portfolio, I implemented instant feedback for every critical action. Whether creating a password, copying it, or deleting an expired one, the system displays a clear toast message, ensuring I always know the outcome of my actions. These notifications not only enhance usability but also reduce errors and save time.
// Toast notification logic
function showToast(message, type) {
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.innerText = message;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), 3000);
}
// Example usage on successful deletion
document.querySelector('.delete-btn').addEventListener('click', () => {
showToast('Password deleted successfully!', 'success');
});
Enhancing security through a strict content-security-policy
Admin panels often perform sensitive operations, making them prime targets for security vulnerabilities like Cross-Site Scripting (XSS). Without a robust Content-Security-Policy, the system risks being compromised.
Dynamic CSP for secure operations
My portfoluo employs a nonce-based CSP to ensure that only trusted scripts execute. This adds an extra layer of protection, especially in admin settings where actions have far-reaching implications. By carefully managing third-party integrations and script permissions, I’ve ensured that the system remains both secure and functional.
# Secure CSP with dynamic nonces
@app.after_request
def apply_csp(response):
nonce = base64.b64encode(secrets.token_bytes(16)).decode('utf-8')
response.headers['Content-Security-Policy'] = (
f"default-src 'self'; script-src 'self' 'nonce-{nonce}'; style-src 'self';"
)
return response
Security and simplicity, hand in hand
The Portfolio Access Management system is more than just a feature; it’s a tailored solution designed to address the unique challenges I face as a professional. From creating time-bound passwords to enabling seamless sharing and efficient management, this system provides a secure yet accessible way to share my portfolio with confidence.
I hope that many others in similar positions can aspire to implement a feature like this on their own portfolios, as it combines practicality with peace of mind. For those interested, I’d be happy to offer support or guidance in developing similar solutions tailored to their specific needs.
This system represents the core functionality, but there’s so much more potential to expand its capabilities. Features like providing selective access to specific case studies and projects, sending automated invitations via email, logging user activities, and even more advanced controls could further enhance the experience. For now, this foundation ensures that security and usability go hand in hand, offering a robust starting point for today’s dynamic professional landscape.