plugins/full-stack-auth/skills/implementing-scalekit-springboot-auth/SKILL.md
Guides Java developers integrating Scalekit OIDC authentication into Spring Boot 3.x apps. Use when the developer mentions Scalekit, enterprise SSO, OIDC login, OAuth2 client setup, protected routes, ID token claims, or logout in a Spring Boot project.
npx skillsauth add scalekit-inc/claude-code-authstack implementing-scalekit-springboot-authInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Scalekit acts as an OIDC provider. Spring Security's oauth2-client starter handles the full
authorization code flow — no custom filters needed.
Add to pom.xml (Spring Boot 3.2+, Java 17+):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Scalekit JSDK -->
<dependency>
<groupId>com.scalekit</groupId>
<artifactId>scalekit-sdk-java</artifactId>
<version>2.0.4</version>
</dependency>
src/main/resources/application.yml:
scalekit:
env-url: ${SCALEKIT_ENV_URL}
client-id: ${SCALEKIT_CLIENT_ID}
client-secret: ${SCALEKIT_CLIENT_SECRET}
redirect-uri: ${SCALEKIT_REDIRECT_URI:http://localhost:8080/login/oauth2/code/scalekit}
spring:
security:
oauth2:
client:
registration:
scalekit:
client-id: ${scalekit.client-id}
client-secret: ${scalekit.client-secret}
authorization-grant-type: authorization_code
redirect-uri: ${scalekit.redirect-uri}
scope: openid,profile,email,offline_access
client-name: Scalekit
provider:
scalekit:
issuer-uri: ${scalekit.env-url}
authorization-uri: ${scalekit.env-url}/oauth/authorize
token-uri: ${scalekit.env-url}/oauth/token
user-info-uri: ${scalekit.env-url}/userinfo
jwk-set-uri: ${scalekit.env-url}/keys
user-name-attribute: sub
For local dev, use application-local.properties — never commit secrets.
@Configuration
public class ScalekitConfig {
@Value("${scalekit.env-url}")
private String envUrl;
@Value("${scalekit.client-id}")
private String clientId;
@Value("${scalekit.client-secret}")
private String clientSecret;
@Bean
public ScalekitClient scalekitClient() {
return new ScalekitClient(envUrl, clientId, clientSecret);
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http,
ClientRegistrationRepository clientRegistrationRepository) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/", "/login", "/error", "/css/**", "/js/**").permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.defaultSuccessUrl("/dashboard", true)
)
.logout(logout -> logout
.logoutSuccessHandler(oidcLogoutSuccessHandler(clientRegistrationRepository))
.invalidateHttpSession(true)
.clearAuthentication(true)
);
return http.build();
}
private LogoutSuccessHandler oidcLogoutSuccessHandler(
ClientRegistrationRepository clientRegistrationRepository) {
OidcClientInitiatedLogoutSuccessHandler handler =
new OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository);
handler.setPostLogoutRedirectUri("{baseUrl}");
return handler;
}
}
@GetMapping("/dashboard")
public String dashboard(@AuthenticationPrincipal OidcUser oidcUser, Model model) {
model.addAttribute("name", oidcUser.getFullName());
model.addAttribute("email", oidcUser.getEmail());
model.addAttribute("subject", oidcUser.getSubject());
model.addAttribute("claims", oidcUser.getClaims());
return "dashboard";
}
Key OidcUser accessors: getFullName(), getEmail(), getSubject(), getClaims(),
getAuthorities().
| Route | Auth? | Notes |
|---|---|---|
| / | No | Home page |
| /login | No | Custom login page |
| /dashboard | Yes | Protected; redirects to login |
| /oauth2/authorization/scalekit | No | Starts OIDC flow |
| /auth/callback | No | Handled by Spring Security automatically |
| /logout | Yes | Triggers OIDC end-session |
- [ ] Get Environment URL (e.g., https://your-env.scalekit.dev)
- [ ] Get Client ID and Client Secret from Settings > API Credentials
- [ ] Add allowed redirect URI: http://localhost:8080/login/oauth2/code/scalekit
- [ ] Optionally add post-logout redirect: http://localhost:8080
Progress:
- [ ] Step 1: Add Maven dependencies
- [ ] Step 2: Add application.yml OAuth2 provider/registration config
- [ ] Step 3: Create ScalekitConfig bean
- [ ] Step 4: Create SecurityConfig filter chain
- [ ] Step 5: Inject @AuthenticationPrincipal OidcUser in protected controllers
- [ ] Step 6: Configure redirect URIs in Scalekit dashboard
- [ ] Step 7: Run app and verify login → dashboard → logout flow
JWKS timeout / JWT verification errors: Spring Security fetches JWKS on every token validation. Increase the decoder timeout — see Spring docs on JWT decoder timeouts.
Redirect URI mismatch: The redirect-uri in application.yml must exactly match what is
registered in the Scalekit dashboard.
Enable debug logging in application.yml:
logging:
level:
org.springframework.security.oauth2: TRACE
com.example.scalekit: DEBUG
Spring Boot does not set SameSite by default. Add to application.yml:
server:
servlet:
session:
cookie:
same-site: lax # Required — 'strict' breaks OAuth callbacks
http-only: true
secure: true # Set to true in production (HTTPS)
Without SameSite: Lax, some browsers drop the session cookie on the cross-origin redirect from Scalekit back to your app, causing the OAuth state to be lost.
The default defaultSuccessUrl("/dashboard", true) ignores the originally requested URL. Remove true to restore the saved-request redirect:
.oauth2Login(oauth2 -> oauth2
.loginPage("/login")
.defaultSuccessUrl("/dashboard") // without `true` — respects saved request
)
Spring Security stores the pre-login URL in the session automatically via RequestCache. The user lands on /dashboard only if no prior request was saved.
Add CORS support in SecurityFilterChain:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http,
ClientRegistrationRepository clientRegistrationRepository) throws Exception {
http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
...
return http.build();
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOrigins(List.of("http://localhost:3000"));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true); // required for session cookies
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
Spring Security redirects unauthenticated requests to the login page by default. Browser AJAX clients expect 401, not 302. Override the entry point in SecurityFilterChain:
.exceptionHandling(ex -> ex
.authenticationEntryPoint((request, response, authException) -> {
String accept = request.getHeader("Accept");
if (accept != null && accept.contains("application/json")) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication required");
} else {
response.sendRedirect("/login");
}
}))
@GetMapping("/dashboard")
public String dashboard(@AuthenticationPrincipal OidcUser oidcUser,
Model model, HttpServletResponse response) {
response.setHeader("Cache-Control", "no-store");
model.addAttribute("name", oidcUser.getFullName());
model.addAttribute("email", oidcUser.getEmail());
return "dashboard";
}
Spring Security disables CSRF protection for OAuth2 login endpoints (/oauth2/authorization/** and /login/oauth2/code/**) by default. The state parameter in the authorization URL serves as the CSRF token for the OAuth flow. You do not need to add any CSRF configuration for basic Scalekit auth.
OidcClientInitiatedLogoutSuccessHandler calls the Scalekit end-session endpoint and revokes the IdP session. If you use a plain logoutSuccessUrl() instead, only the local Spring session is cleared — the user will be silently re-authenticated on the next login attempt. Always use the OIDC handler.
development
Walks through a structured production readiness checklist for Scalekit SSO implementations. Use when the user says they are going live, launching to production, doing a pre-launch review, hardening their SSO setup, or wants to verify their Scalekit implementation is production-ready.
data-ai
Implements complete SSO and authentication flows using Scalekit. Handles modular SSO, IdP-initiated login, user session management, and enterprise customer onboarding. Use when adding authentication, SSO, SAML, OIDC, or user login to applications.
testing
Implements Scalekit's admin portal for customer self-serve SSO and SCIM configuration. Generates portal links server-side and embeds the portal as an iframe in the app's settings UI. Use when the user asks to add an admin portal, customer self-serve SSO setup, iframe embed for SSO config, shareable setup link, or let customers configure their own SSO or SCIM connection.
development
Walks through a structured production readiness checklist for Scalekit SCIM provisioning implementations. Use when the user says they are going live, launching to production, doing a pre-launch review, or wants to verify their SCIM directory sync implementation is production-ready.