OAuth SSO Implementation Plan for Google/GitHub Logins
Executive Summary
Wikantik's JAAS-based architecture is **well-suited for OAuth integration**. The key insight is that passwords are optional in the user database, so OAuth users can be created without passwords and never use password-based login.
---
Required Components
1. 1. New Java Classes (~6-8 files)
| File | Purpose |
|------|---------|
| `OAuthLoginModule.java` | JAAS LoginModule that validates tokens and creates/links users |
| `OAuthCallbackHandler.java` | Passes OAuth token to LoginModule via JAAS callbacks |
| `OAuthCallback.java` | Custom JAAS Callback for OAuth data |
| `OAuthCallbackServlet.java` | Handles `/wiki/oauth/callback` redirect from providers |
| `GoogleOAuthProvider.java` | Validates Google tokens, fetches user info |
| `GitHubOAuthProvider.java` | Validates GitHub tokens, fetches user info |
| `OAuthUserInfo.java` | DTO for user info from providers |
1. 2. UI Modifications
- **LoginContent.jsp**: Add "Login with Google" and "Login with GitHub" buttons
- Buttons link to `/wiki/oauth/google?redirect=...` and `/wiki/oauth/github?redirect=...`
1. 3. Configuration (wikantik.properties)
```properties
OAuth SSO Configuration
jspwiki.oauth.enabled=true
jspwiki.oauth.autoCreateUsers=true
Google OAuth 2.0 / OpenID Connect
jspwiki.oauth.google.enabled=true
jspwiki.oauth.google.clientId=YOUR_CLIENT_ID
jspwiki.oauth.google.clientSecret=YOUR_CLIENT_SECRET
GitHub OAuth 2.0
jspwiki.oauth.github.enabled=true
jspwiki.oauth.github.clientId=YOUR_CLIENT_ID
jspwiki.oauth.github.clientSecret=YOUR_CLIENT_SECRET
```
1. 4. Dependencies (pom.xml)
- OAuth 2.0 client library (e.g., `google-oauth-client` or `oltu.oauth2`)
- JWT validation (for Google ID tokens)
- HTTP client for API calls
---
OAuth Flow Integration
```
User clicks "Login with Google" on LoginContent.jsp
↓
2. OAuthCallbackServlet redirects to Google consent screen
↓
3. User authenticates with Google
↓
4. Google redirects to /wiki/oauth/callback?code=...&state=...
↓
5. OAuthCallbackServlet:
- Exchanges code for access token
- Fetches user info (email, name, provider ID)
- Creates OAuthCallbackHandler with token data
- Calls AuthenticationManager with OAuthLoginModule
↓
6. OAuthLoginModule.login():
- Gets token data from callback handler
- Looks up user by email in UserDatabase
- If not found: creates new UserProfile (no password)
- Adds WikiPrincipal to principals set
↓
7. WikiSession.actionPerformed():
- Receives LOGIN_AUTHENTICATED event
- Sets session status to AUTHENTICATED
- Injects user profile principals
- Injects group memberships
↓
8. Redirect to original page
```
---
User Account Creation Strategy
Option A: Email-Based Linking (Recommended)
```java
// In OAuthLoginModule.login()
UserProfile profile;
try {
profile = db.findByEmail(oauthUserInfo.getEmail());
} catch (NoSuchPrincipalException e) {
// Create new user
profile = db.newProfile();
profile.setLoginName(generateLoginName(oauthUserInfo));
profile.setEmail(oauthUserInfo.getEmail());
profile.setFullname(oauthUserInfo.getName());
profile.setPassword(null); // No password for OAuth users
// Store OAuth metadata in custom attributes
profile.getAttributes().put("oauth.provider", "google");
profile.getAttributes().put("oauth.providerId", oauthUserInfo.getId());
db.save(profile);
}
```
Login Name Generation Options
- `google_123456789` (provider + ID)
- `john.smith` (email local part)
- `jsmith` (truncated)
---
Key Technical Insights
1. 1. Password-less Users Work Out-of-Box
`UserDatabaseLoginModule.login()` line 86 checks:
```java
if (storedPassword != null && db.validatePassword(...))
```
OAuth users with `null` password bypass password validation entirely.
1. 2. Session Establishment is Automatic
Just fire the event and WikiSession handles everything:
```java
fireEvent(WikiSecurityEvent.LOGIN_AUTHENTICATED, principal, session);
```
1. 3. Configuration is Already Flexible
`DefaultAuthenticationManager.initLoginModuleOptions()` loads all `jspwiki.loginModule.options.*` properties and passes them to the LoginModule.
1. 4. User Database Abstraction Works Well
The `JDBCUserDatabase` supports:
- `findByEmail(String)` - Look up existing users
- `save(UserProfile)` - Create new users
- Custom attributes map for OAuth metadata
---
Challenges & Solutions
| Challenge | Solution |
|-----------|----------|
| **Same person, multiple providers** | Link by email; store provider info in attributes |
| **Incomplete profile data** | Compute full name from email; prompt for completion |
| **Token expiry** | Store refresh token (encrypted) in attributes |
| **Logout coordination** | Clear local session; optionally revoke provider token |
| **Account security** | Require email verification; admin account linking tool |
---
Estimated Effort
| Phase | Effort | Description |
|-------|--------|-------------|
| **Core OAuth** | 3-4 days | LoginModule, CallbackHandler, Servlet, Providers |
| **User provisioning** | 1-2 days | Account creation, email linking, attributes |
| **UI** | 1 day | Login buttons, styling |
| **Configuration** | 0.5 day | Properties, documentation |
| **Testing** | 2-3 days | Unit tests, integration tests |
| **Total** | ~8-10 days | For experienced developer |
---
Architecture Decision: Servlet + LoginModule Hybrid
- Recommended approach**:
1. **OAuthCallbackServlet** handles:
- OAuth redirect flow
- Authorization code exchange
- Token validation
- Calling provider APIs
2. **OAuthLoginModule** handles:
- User lookup/creation in database
- Principal establishment
- JAAS integration
This separation keeps OAuth protocol details out of JAAS and allows the LoginModule to focus on user management.
---
Files to Modify (Existing)
| File | Change |
|------|--------|
| `wikantik.properties` | Add OAuth configuration properties |
| `LoginContent.jsp` | Add OAuth login buttons |
| `web.xml` | Register OAuthCallbackServlet |
| `pom.xml` | Add OAuth dependencies |
---
Key Code Locations
Authentication Flow
- Entry: `WikiServletFilter.doFilter()` line 120
- Manager: `DefaultAuthenticationManager.login()` lines 156-240
- Session: `WikiSession.getWikiSession()` line 483
- Event handling: `WikiSession.actionPerformed()` line 244
User Management
- Profile interface: `/auth/user/UserProfile.java`
- Profile impl: `/auth/user/DefaultUserProfile.java`
- Database interface: `/auth/user/UserDatabase.java`
- Database impl: `/auth/user/JDBCUserDatabase.java`
Login Modules
- Abstract: `/auth/login/AbstractLoginModule.java`
- User database: `/auth/login/UserDatabaseLoginModule.java` lines 66-108
- Callback base: `/auth/login/WikiCallbackHandler.java`
Configuration
- Properties loaded: `DefaultAuthenticationManager.initialize()` line 110
- Options extraction: `DefaultAuthenticationManager.initLoginModuleOptions()` line 375
UI
- Login page: `/wikantik-war/src/main/webapp/Login.jsp`
- Login form: `/wikantik-war/src/main/webapp/templates/default/LoginContent.jsp`
---
Conclusion
Implementing OAuth SSO for Google/GitHub in Wikantik is **architecturally straightforward** due to:
1. **Pluggable JAAS LoginModules** - No changes to auth framework needed
2. **Optional passwords** - OAuth users created without passwords work perfectly
3. **Automatic session setup** - Fire event, session handles the rest
4. **Flexible user database** - Custom attributes for OAuth metadata
The main work is implementing the OAuth protocol (token exchange, API calls) and user provisioning logic. The Wikantik authentication infrastructure supports this cleanly.