Sakai’s APIs use session-based authentication to secure access to learning management resources. This guide covers authentication mechanisms, session management, and security best practices.
Authentication Overview
Sakai uses a multi-layered authentication system:
- AuthenticationManager - Validates user credentials
- SessionManager - Creates and manages user sessions
- UsageSessionService - Tracks session usage and events
- Session Cookies - Maintains session state across requests
Login Endpoint
The primary authentication endpoint is /api/login, which accepts username and password credentials.
Login Request
Endpoint: POST /api/login
Request Body:
{
"username": "student1",
"password": "secretpassword"
}
Response (Success):
"ABC123-SESSION-ID-XYZ789"
Response (Error):
"username and password are required"
Login Implementation
The login controller performs the following steps:
// webapi/src/main/java/org/sakaiproject/webapi/controllers/LoginController.java:62
@PostMapping("/login")
public ResponseEntity<String> login(
LoginRestBean loginBean,
HttpServletRequest request,
HttpServletResponse response
) throws AuthenticationException {
String username = loginBean.getUsername();
String password = loginBean.getPassword();
// Validate input
if (StringUtils.isAnyBlank(username, password)) {
return ResponseEntity.badRequest()
.body("username and password are required");
}
String ipAddress = request.getRemoteAddr();
// Create authentication evidence
Evidence e = new IdPwEvidence(username, password, ipAddress);
// Authenticate user
Authentication a = authenticationManager.authenticate(e);
// Start new session
Session s = sessionManager.startSession();
if (s == null) {
log.warn("/api/login failed to establish session");
return new ResponseEntity<>(
"Unable to establish session",
HttpStatus.INTERNAL_SERVER_ERROR
);
}
// Set current session
sessionManager.setCurrentSession(s);
// Log usage session
usageSessionService.login(
a.getUid(),
username,
ipAddress,
"/api/login",
UsageSessionService.EVENT_LOGIN_WS
);
// Create session cookie
Cookie c = new Cookie("JSESSIONID", s.getId() + "." + suffix);
c.setPath("/");
c.setMaxAge(-1);
if (request.isSecure()) {
c.setSecure(true);
}
response.addCookie(c);
return ResponseEntity.ok(s.getId());
}
Session Management
Sakai sessions are managed by the SessionManager service, which provides session creation, retrieval, and lifecycle management.
Session Interface
The Session interface provides access to session data:
// kernel/api/src/main/java/org/sakaiproject/tool/api/Session.java:32
public interface Session {
// Session identification
String getId();
// User information
String getUserId();
String getUserEid();
// Session lifecycle
long getCreationTime();
long getLastAccessedTime();
int getMaxInactiveInterval();
void invalidate();
void clear();
// Session attributes
Object getAttribute(String name);
Enumeration<String> getAttributeNames();
void setAttribute(String name, Object value);
void removeAttribute(String name);
// Tool sessions
ToolSession getToolSession(String placementId);
ContextSession getContextSession(String contextId);
}
SessionManager Interface
// kernel/api/src/main/java/org/sakaiproject/tool/api/SessionManager.java:34
public interface SessionManager {
// Session creation
Session startSession();
Session startSession(String id);
// Session retrieval
Session getSession(String sessionId);
List<Session> getSessions();
// Current session management
Session getCurrentSession();
String getCurrentSessionUserId();
void setCurrentSession(Session s);
// Tool session management
ToolSession getCurrentToolSession();
void setCurrentToolSession(ToolSession s);
// Session monitoring
int getActiveUserCount(int secs);
// Session ID generation
String makeSessionId(HttpServletRequest req, Principal principal);
}
Session Validation
All API controllers should validate the session before processing requests:
// webapi/src/main/java/org/sakaiproject/webapi/controllers/AbstractSakaiApiController.java:56
Session checkSakaiSession() {
try {
Session session = sessionManager.getCurrentSession();
// Verify session has authenticated user
if (StringUtils.isBlank(session.getUserId())) {
log.error("Sakai user session is invalid");
throw new MissingSessionException();
}
return session;
} catch (IllegalStateException e) {
log.error("Could not retrieve the sakai session");
throw new MissingSessionException(e.getCause());
}
}
Usage in Controllers:
// webapi/src/main/java/org/sakaiproject/webapi/controllers/SitesController.java:62
@GetMapping(value = "/users/{userId}/sites")
public Map<String, List<Map<String, Object>>> getSites(
@PathVariable String userId,
@RequestParam Optional<Boolean> pinned
) {
// Validate session before processing
checkSakaiSession();
// Process request...
}
Authentication Manager
The AuthenticationManager validates user credentials:
// kernel/api/src/main/java/org/sakaiproject/user/api/AuthenticationManager.java:29
public interface AuthenticationManager {
/**
* Attempt to authenticate a user by the given evidence.
* Success produces the authenticated user id.
* Failure throws an exception.
*
* @param e The collected evidence to authenticate.
* @return The authentication information if authenticated.
* @throws AuthenticationException if the evidence is not
* understood or not valid.
*/
Authentication authenticate(Evidence e)
throws AuthenticationException;
}
Evidence Types
Username/Password Evidence:
// kernel/api/src/main/java/org/sakaiproject/util/IdPwEvidence.java
Evidence e = new IdPwEvidence(username, password, ipAddress);
External/Trusted Evidence:
For SSO and trusted authentication sources:
// kernel/api/src/main/java/org/sakaiproject/util/ExternalTrustedEvidence.java
Evidence e = new ExternalTrustedEvidence(...);
Basic Auth Evidence:
// kernel/api/src/main/java/org/sakaiproject/util/BasicAuth.java
Evidence e = new BasicAuth(...);
Session Cookies
Sakai uses the JSESSIONID cookie (configurable) to maintain session state. The cookie name and domain can be configured via system properties.
Cookie Configuration
// webapi/src/main/java/org/sakaiproject/webapi/controllers/LoginController.java:97
String cookieName = "JSESSIONID";
// Retrieve configured cookie name
if (System.getProperty(RequestFilter.SAKAI_COOKIE_PROP) != null) {
cookieName = System.getProperty(RequestFilter.SAKAI_COOKIE_PROP);
}
// Compute session cookie suffix for load balancing
String suffix = System.getProperty(RequestFilter.SAKAI_SERVERID);
if (StringUtils.isEmpty(suffix)) {
suffix = "sakai";
}
// Create cookie
Cookie c = new Cookie(cookieName, s.getId() + "." + suffix);
c.setPath("/");
c.setMaxAge(-1); // Session cookie
// Configure domain if specified
if (System.getProperty(RequestFilter.SAKAI_COOKIE_DOMAIN) != null) {
c.setDomain(System.getProperty(RequestFilter.SAKAI_COOKIE_DOMAIN));
}
// Secure cookie for HTTPS
if (request.isSecure()) {
c.setSecure(true);
}
response.addCookie(c);
System Properties
sakai.cookieName - Cookie name (default: JSESSIONID)
sakai.cookieDomain - Cookie domain
sakai.serverId - Server ID for load balancing
Request Filtering
The RequestFilter processes all requests and manages session context:
// webapi/src/main/java/org/sakaiproject/webapi/WebAppConfiguration.java:41
servletContext.addFilter("sakai.request", RequestFilter.class)
.addMappingForUrlPatterns(
EnumSet.of(
DispatcherType.REQUEST,
DispatcherType.FORWARD,
DispatcherType.INCLUDE
),
true,
"/*"
);
The filter:
- Extracts session ID from cookies or headers
- Sets the current session in
SessionManager
- Cleans up session after request processing
Client Authentication Flow
Step 1: Login
curl -X POST https://sakai.example.edu/api/login \
-H "Content-Type: application/json" \
-d '{
"username": "student1",
"password": "password123"
}' \
-c cookies.txt
Response:
The session cookie is saved to cookies.txt.
Step 2: Make Authenticated Requests
curl https://sakai.example.edu/api/user/roles \
-b cookies.txt
Response:
Step 3: Access User’s Sites
curl https://sakai.example.edu/api/users/student1/sites \
-b cookies.txt
Response:
{
"terms": [...],
"sites": [
{
"siteId": "site123",
"title": "Introduction to Computer Science",
"url": "/portal/site/site123",
"pinned": true,
"tools": [...]
}
]
}
Security Best Practices
Security Recommendations:
- Always use HTTPS in production
- Set secure cookie flags for HTTPS connections
- Implement proper session timeout policies
- Log authentication attempts for audit purposes
- Validate session on every API request
- Never expose session IDs in URLs
Session Timeout
Configure session timeout via:
session.setMaxInactiveInterval(3600); // 1 hour in seconds
Session Cleanup
Invalidate sessions when no longer needed:
Session session = sessionManager.getCurrentSession();
session.invalidate();
IP Address Tracking
Authentication includes IP address logging:
String ipAddress = request.getRemoteAddr();
Evidence e = new IdPwEvidence(username, password, ipAddress);
Usage Session Tracking
The UsageSessionService tracks session activity:
// webapi/src/main/java/org/sakaiproject/webapi/controllers/LoginController.java:92
usageSessionService.login(
a.getUid(), // User ID
username, // Username
ipAddress, // IP Address
"/api/login", // Login path
UsageSessionService.EVENT_LOGIN_WS // Event type
);
This enables:
- User activity monitoring
- Presence tracking
- Event logging
- Session analytics
Troubleshooting
Invalid Session Errors
Error: MissingSessionException
Causes:
- Session expired
- Invalid session cookie
- User not authenticated
Solution: Re-authenticate via /api/login
Authentication Failures
Error: AuthenticationException
Causes:
- Invalid credentials
- Account locked
- Authentication service unavailable
Solution: Verify credentials and account status
Cookie Issues
Problem: Session cookie not persisting
Checks:
- Verify cookie domain matches request domain
- Check HTTPS/secure flag settings
- Verify cookie path is
/
- Check browser cookie settings
Next Steps