Add authentication system with Authelia integration
- Add /auth/user API endpoint to extract and return Authelia headers - Create account.html test page for authentication verification - Update documentation in CLAUDE.md with authentication setup details - Add account page to main feature list in index.html 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
94e6b88cbe
commit
e95f47874a
4 changed files with 143 additions and 0 deletions
24
CLAUDE.md
24
CLAUDE.md
|
@ -18,6 +18,30 @@ There is a reverse proxy rewrite rule in place. To call any API endpoint from th
|
|||
- API endpoint URLs should be: `playground.shiny.space/api/yourendpointhere`
|
||||
- Example: `/randomnumber` endpoint becomes `/api/randomnumber`
|
||||
|
||||
## Authentication
|
||||
The setup uses Caddy with Authelia for authentication:
|
||||
- Caddy acts as reverse proxy and checks with Authelia before allowing access to protected pages
|
||||
- When authenticated, Authelia adds headers to requests that reach the Go API
|
||||
- The Go API can read these headers to get user information
|
||||
|
||||
### Authentication Headers from Authelia
|
||||
The following headers are available in authenticated requests:
|
||||
- `Remote-User`: Username of the authenticated user
|
||||
- `Remote-Name`: Display name of the user
|
||||
- `Remote-Email`: Email address of the user
|
||||
- `Remote-Groups`: User's group memberships
|
||||
|
||||
### Authentication API Endpoint
|
||||
- `/api/auth/user` (GET) - Returns user information and all headers for debugging
|
||||
- Protected pages can use JavaScript to fetch this endpoint to get current user info
|
||||
- Returns JSON with username, name, email, groups, and complete headers object
|
||||
|
||||
### Authentication Test Page
|
||||
- `account.html` - Test page that requires authentication to access
|
||||
- Displays user information by calling `/api/auth/user`
|
||||
- Shows both structured user info and raw headers for debugging
|
||||
- Configure Caddy to require authentication for this page to test the setup
|
||||
|
||||
## Development
|
||||
To rebuild the Go API after making changes:
|
||||
- Run `go build -o api` in the project directory
|
||||
|
|
77
account.html
Normal file
77
account.html
Normal file
|
@ -0,0 +1,77 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Account Information</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>Account Information</h1>
|
||||
<p>Your authenticated user information:</p>
|
||||
|
||||
<div class="section">
|
||||
<h3>User Details</h3>
|
||||
<div id="user-info" class="loading">Loading user information...</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>All Headers (Debug)</h3>
|
||||
<div id="headers-info" class="loading">Loading headers...</div>
|
||||
</div>
|
||||
|
||||
<div class="back-link">
|
||||
<a href="index.html" class="back-button">← Back to Feature List</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function loadUserInfo() {
|
||||
try {
|
||||
const response = await fetch('/api/auth/user');
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
// Display user information
|
||||
const userInfoDiv = document.getElementById('user-info');
|
||||
userInfoDiv.innerHTML = `
|
||||
<p><strong>Username:</strong> ${data.username || 'Not provided'}</p>
|
||||
<p><strong>Display Name:</strong> ${data.name || 'Not provided'}</p>
|
||||
<p><strong>Email:</strong> ${data.email || 'Not provided'}</p>
|
||||
<p><strong>Groups:</strong> ${data.groups || 'Not provided'}</p>
|
||||
`;
|
||||
userInfoDiv.className = 'response';
|
||||
|
||||
// Display all headers
|
||||
const headersDiv = document.getElementById('headers-info');
|
||||
let headersHtml = '<div style="font-family: monospace; font-size: 14px; background: #f8f8f8; padding: 15px; border-radius: 4px; overflow-x: auto;">';
|
||||
|
||||
if (data.headers && Object.keys(data.headers).length > 0) {
|
||||
for (const [key, value] of Object.entries(data.headers)) {
|
||||
headersHtml += `<div><strong>${key}:</strong> ${value}</div>`;
|
||||
}
|
||||
} else {
|
||||
headersHtml += '<div>No headers available</div>';
|
||||
}
|
||||
|
||||
headersHtml += '</div>';
|
||||
headersDiv.innerHTML = headersHtml;
|
||||
headersDiv.className = 'response';
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error loading user info:', error);
|
||||
|
||||
document.getElementById('user-info').innerHTML = `<p class="error">Error loading user information: ${error.message}</p>`;
|
||||
document.getElementById('user-info').className = 'error';
|
||||
|
||||
document.getElementById('headers-info').innerHTML = `<p class="error">Error loading headers: ${error.message}</p>`;
|
||||
document.getElementById('headers-info').className = 'error';
|
||||
}
|
||||
}
|
||||
|
||||
// Load user info when page loads
|
||||
document.addEventListener('DOMContentLoaded', loadUserInfo);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -27,6 +27,10 @@
|
|||
<a href="items.html">Items Management</a>
|
||||
<div class="description">Full CRUD demo with SQLite database - create, view, and delete items with persistent storage.</div>
|
||||
</li>
|
||||
<li>
|
||||
<a href="account.html">Account Information</a>
|
||||
<div class="description">Authentication test page - displays user info and headers from Authelia (requires authentication).</div>
|
||||
</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
|
|
38
main.go
38
main.go
|
@ -21,6 +21,14 @@ type Item struct {
|
|||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
Username string `json:"username"`
|
||||
Name string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
Groups string `json:"groups"`
|
||||
Headers map[string]string `json:"headers"`
|
||||
}
|
||||
|
||||
var db *sql.DB
|
||||
|
||||
func initDB() {
|
||||
|
@ -81,6 +89,9 @@ func main() {
|
|||
// Items API endpoints
|
||||
http.HandleFunc("/items", handleItems)
|
||||
http.HandleFunc("/items/", handleItemDelete)
|
||||
|
||||
// Auth endpoints
|
||||
http.HandleFunc("/auth/user", handleAuthUser)
|
||||
|
||||
log.Println("API running on :3847")
|
||||
http.ListenAndServe("127.0.0.1:3847", nil)
|
||||
|
@ -224,3 +235,30 @@ func handleItemDelete(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
json.NewEncoder(w).Encode(map[string]string{"message": "Item deleted successfully"})
|
||||
}
|
||||
|
||||
func handleAuthUser(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
if r.Method != "GET" {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
json.NewEncoder(w).Encode(map[string]string{"error": "Method not allowed"})
|
||||
return
|
||||
}
|
||||
|
||||
// Collect all headers for debugging
|
||||
headers := make(map[string]string)
|
||||
for name, values := range r.Header {
|
||||
headers[name] = strings.Join(values, ", ")
|
||||
}
|
||||
|
||||
// Extract common Authelia headers
|
||||
userInfo := UserInfo{
|
||||
Username: r.Header.Get("Remote-User"),
|
||||
Name: r.Header.Get("Remote-Name"),
|
||||
Email: r.Header.Get("Remote-Email"),
|
||||
Groups: r.Header.Get("Remote-Groups"),
|
||||
Headers: headers,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(userInfo)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue