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
12 changes: 11 additions & 1 deletion development/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
services:
# Lightweight Keycloak server for local development.
keycloak:
image: ghcr.io/healthcare-monitoring-system/d4-platform-devops:latest
image: quay.io/keycloak/keycloak:26.6.1
command: start-dev --import-realm
environment:
KC_BOOTSTRAP_ADMIN_USERNAME: admin
KC_BOOTSTRAP_ADMIN_PASSWORD: admin
ports:
- "8080:8080"
volumes:
- ./keycloak/themes:/opt/keycloak/themes:ro
- ./keycloak/realm-config.json:/opt/keycloak/data/import/realm-config.json:ro
depends_on:
postgres:
condition: service_healthy

# Your Backend Service
backend:
Expand All @@ -25,3 +32,6 @@ services:
- "3000:3000"
depends_on:
- backend

volumes:
postgres_data:
175 changes: 94 additions & 81 deletions development/keycloak/realm-config.json
Original file line number Diff line number Diff line change
@@ -1,89 +1,102 @@
{
"realm": "HealthCare",
"displayNameHtml": "<h1 style=\"font-size: 40pt; font-weight: 400;\">MediCare Sign-In</h1>",
"enabled": true,
"registrationAllowed": true,
"registrationEmailAsUsername": false,
"rememberMe": true,
"verifyEmail": false,
"resetPasswordAllowed": true,
"editUsernameAllowed": false,
"users": [
"realm": "HealthCare",
"displayNameHtml": "<h1 style=\"font-size: 40pt; font-weight: 400;\">MediCare Sign-In</h1>",
"enabled": true,
"registrationAllowed": true,
"registrationEmailAsUsername": false,
"rememberMe": true,
"loginTheme": "medicare",
"verifyEmail": false,
"resetPasswordAllowed": true,
"editUsernameAllowed": false,
"users": [
{
"username": "Admin",
"enabled": true,
"emailVerified": true,
"credentials": [
{
"username": "Admin",
"enabled": true,
"emailVerified": true,
"credentials": [
{
"type": "password",
"value": "admin"
}
],
"realmRoles": [
"admin"
]
},
{
"username": "Doctor1",
"enabled": true,
"emailVerified": true,
"credentials": [
{
"type": "password",
"value": "user123"
}
],
"realmRoles": [
"doctor"
]
},
"type": "password",
"value": "admin"
}
],
"realmRoles": [
"admin"
]
},
{
"username": "Doctor1",
"enabled": true,
"emailVerified": true,
"credentials": [
{
"username": "test",
"email": "test@example.com",
"firstName": "Test",
"lastName": "User",
"enabled": true,
"emailVerified": true,
"credentials": [
{
"type": "password",
"value": "test"
}
],
"realmRoles": [
"patient"
]
"type": "password",
"value": "user123"
}
],
"roles": {
"realm": [
{
"name": "admin",
"description": "Administrative role for the healthcare system"
},
{
"name": "doctor",
"description": "Role for doctors in the healthcare system"
},
{
"name": "patient",
"description": "Role for patients in the healthcare system"
}
]
],
"realmRoles": [
"doctor"
]
},
"clients": [
{
"username": "test",
"email": "test@example.com",
"firstName": "Test",
"lastName": "User",
"enabled": true,
"emailVerified": true,
"credentials": [
{
"clientId": "medicare",
"publicClient": false,
"standardFlowEnabled": true,
"directAccessGrantsEnabled": false,
"redirectUris": [
"http://localhost:3000/*"
],
"webOrigins": [
"http://localhost:3000/*",
"http://localhost:8000/*"
]
"type": "password",
"value": "test"
}
],
"realmRoles": [
"patient"
]
}
],
"roles": {
"realm": [
{
"name": "admin",
"description": "Role for administrators in the healthcare system"
},
{
"name": "doctor",
"description": "Role for doctors in the healthcare system"
},
{
"name": "patient",
"description": "Role for patients in the healthcare system"
}
]
}
},
"clients": [
{
"clientId": "medicare",
"name": "MediCare Frontend",
"secret": "OVERRIDE-THIS-CLIENT-SECRET",
"baseUrl": "http://127.0.0.1:3000",
"publicClient": false,
"standardFlowEnabled": true,
"directAccessGrantsEnabled": false,
"redirectUris": [
"http://localhost:3000/*",
"http://127.0.0.1:3000/*",
"http://127.0.0.1:8000/api/auth/callback",
"http://localhost:8000/api/auth/callback",
"http://localhost:3002/*"
],
"attributes": {
"post.logout.redirect.uris": "http://localhost:3000/login##http://127.0.0.1:3000/login##http://localhost:3002/login"
},
"webOrigins": [
"http://localhost:3000",
"http://127.0.0.1:3000",
"http://localhost:3002",
"http://localhost:8000"
]
}
]
}
27 changes: 27 additions & 0 deletions development/keycloak/themes/medicare/login/error.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!doctype html>
<html lang="${(locale.currentLanguageTag)!'en'}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MediCare Sign-In Error</title>
<link rel="stylesheet" href="${url.resourcesPath}/css/login.css">
</head>
<body>
<main class="mc-page">
<section class="mc-card" aria-labelledby="kc-page-title">
<header class="mc-header">
<div class="mc-badge" aria-hidden="true">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v4m0 4h.01M10.3 4.2L2.5 18a2 2 0 001.7 3h15.6a2 2 0 001.7-3L13.7 4.2a2 2 0 00-3.4 0z"/>
</svg>
</div>
<p class="mc-brand">MediCare</p>
<h1 id="kc-page-title" class="mc-title">Sign-In Error</h1>
<p class="mc-copy">${kcSanitize((message.summary)!'Unable to complete sign-in.')?no_esc}</p>
</header>

<a class="mc-button" href="${url.loginUrl}">Back to MediCare Sign-In</a>
</section>
</main>
</body>
</html>
38 changes: 38 additions & 0 deletions development/keycloak/themes/medicare/login/info.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!doctype html>
<html lang="${(locale.currentLanguageTag)!'en'}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MediCare</title>
<link rel="stylesheet" href="${url.resourcesPath}/css/login.css">
</head>
<body>
<main class="mc-page">
<section class="mc-card" aria-labelledby="kc-page-title">
<header class="mc-header">
<div class="mc-badge" aria-hidden="true">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-5"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3l7 3v5c0 4.4-2.8 8.4-7 10-4.2-1.6-7-5.6-7-10V6l7-3z"/>
</svg>
</div>
<p class="mc-brand">MediCare</p>
<h1 id="kc-page-title" class="mc-title">Account Notice</h1>
</header>

<#if messageHeader??>
<div class="mc-message mc-message-success">${kcSanitize(messageHeader)?no_esc}</div>
</#if>
<#if message?has_content>
<div class="mc-message mc-message-${message.type}">${kcSanitize(message.summary)?no_esc}</div>
</#if>

<#if actionUri??>
<a class="mc-button" href="${actionUri}">${msg("proceedWithAction")}</a>
<#else>
<a class="mc-button" href="${url.loginUrl}">Back to sign in</a>
</#if>
</section>
</main>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<!doctype html>
<html lang="${(locale.currentLanguageTag)!'en'}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Reset MediCare Password</title>
<link rel="stylesheet" href="${url.resourcesPath}/css/login.css">
</head>
<body>
<main class="mc-page">
<section class="mc-card" aria-labelledby="kc-page-title">
<header class="mc-header">
<div class="mc-badge" aria-hidden="true">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 7a2 2 0 012 2m4 0a6 6 0 01-7.7 5.8L11 17H8v3H5v-3H2v-3l6.2-6.2A6 6 0 1121 9z"/>
</svg>
</div>
<p class="mc-brand">MediCare</p>
<h1 id="kc-page-title" class="mc-title">Reset Password</h1>
<p class="mc-copy">Enter your username or email and we will send recovery instructions if the account exists.</p>
</header>

<#if message?has_content>
<div class="mc-message mc-message-${message.type}" role="alert">${kcSanitize(message.summary)?no_esc}</div>
</#if>

<form class="mc-form" action="${url.loginAction}" method="post">
<div class="mc-field">
<label class="mc-label" for="username"><#if !realm.loginWithEmailAllowed>Username<#elseif !realm.registrationEmailAsUsername>Username or email<#else>Email</#if></label>
<input class="mc-input" id="username" name="username" type="text" autofocus autocomplete="username" value="${(auth.attemptedUsername!'')}">
</div>
<button class="mc-button" type="submit">Send Reset Instructions</button>
</form>

<p class="mc-footer"><a class="mc-link" href="${url.loginUrl}">Back to sign in</a></p>
</section>
</main>
</body>
</html>
63 changes: 63 additions & 0 deletions development/keycloak/themes/medicare/login/login.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!doctype html>
<html lang="${(locale.currentLanguageTag)!'en'}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${(realm.displayName)!'MediCare'} Sign In</title>
<link rel="stylesheet" href="${url.resourcesPath}/css/login.css">
</head>
<body>
<main class="mc-page">
<section class="mc-card" aria-labelledby="kc-page-title">
<header class="mc-header">
<div class="mc-badge" aria-hidden="true">
<svg width="22" height="22" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3l7 3v5c0 4.4-2.8 8.4-7 10-4.2-1.6-7-5.6-7-10V6l7-3z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-5"/>
</svg>
</div>
<p class="mc-brand">MediCare</p>
<h1 id="kc-page-title" class="mc-title">Secure Sign In</h1>
<p class="mc-copy">Use your MediCare account to access patient monitoring, reports, and administrative tools.</p>
</header>

<#if message?has_content>
<div class="mc-message mc-message-${message.type}" role="alert">${kcSanitize(message.summary)?no_esc}</div>
</#if>

<form class="mc-form" id="kc-form-login" action="${url.loginAction}" method="post">
<#if !usernameHidden??>
<div class="mc-field">
<label class="mc-label" for="username"><#if !realm.loginWithEmailAllowed>Username<#elseif !realm.registrationEmailAsUsername>Username or email<#else>Email</#if></label>
<input class="mc-input" id="username" name="username" type="text" value="${(login.username!'')}" autocomplete="username" autofocus>
</div>
</#if>

<div class="mc-field">
<label class="mc-label" for="password">Password</label>
<input class="mc-input" id="password" name="password" type="password" autocomplete="current-password">
</div>

<div class="mc-row">
<#if realm.rememberMe && !usernameHidden??>
<label class="mc-check">
<input id="rememberMe" name="rememberMe" type="checkbox" <#if login.rememberMe??>checked</#if>>
Remember me
</label>
</#if>
<#if realm.resetPasswordAllowed>
<a class="mc-link" href="${url.loginResetCredentialsUrl}">Forgot password?</a>
</#if>
</div>

<input type="hidden" id="id-hidden-input" name="credentialId" <#if auth.selectedCredential?has_content>value="${auth.selectedCredential}"</#if>>
<button class="mc-button" type="submit">Sign In</button>
</form>

<#if realm.registrationAllowed && !registrationDisabled??>
<p class="mc-footer">Need an account? <a class="mc-link" href="${url.registrationUrl}">Create one</a></p>
</#if>
</section>
</main>
</body>
</html>
Loading
Loading