implementing-scalekit-springboot-auth
Scalekit Auth in Spring Boot
Scalekit acts as an OIDC provider. Spring Security's oauth2-client starter handles the full
authorization code flow — no custom filters needed.
Required dependencies
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>
Configuration
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.
Scalekit SDK bean
@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);
}
}
Security filter chain
@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;
}
}
Accessing user identity in controllers
@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().
Application routes
| 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 |
Scalekit Dashboard setup checklist
- [ ] 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
Workflows
Add Scalekit auth to an existing Spring Boot app
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
Troubleshooting
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
Reference
- Full working example: scalekit-inc/scalekit-springboot-auth-example
- Scalekit docs: https://docs.scalekit.com
- Spring Security OAuth2 docs: https://docs.spring.io/spring-security/reference/servlet/oauth2
Tactics
SameSite=Lax on the session cookie
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.
Deep link preservation — use SavedRequestAwareAuthenticationSuccessHandler
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.
CORS for browser clients
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;
}
AJAX: 401 instead of redirect
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");
}
}))
Cache-Control: no-store on protected responses
@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";
}
CSRF and OAuth2 — what Spring Security does automatically
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.
OIDC logout vs local logout
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.
More from scalekit-inc/skills
setup-scalekit
Use when a developer is new to Scalekit and needs guidance on where to start, doesn't know which auth plugin or skill to choose, wants to connect an AI agent or agentic workflow to third-party services (Gmail, Slack, Notion, Google Calendar), needs OAuth or tool-calling auth for agents, wants to add authentication to a project but hasn't chosen an approach yet, or needs to install the Scalekit plugin for their AI coding tool (Claude Code, Codex, Copilot CLI, Cursor, or other agents).
11implementing-scalekit-fsa
Implements Scalekit full-stack authentication (FSA) including sign-up, login, logout, and secure session management using JWT tokens. Use when building or integrating user authentication with the Scalekit SDK across Node.js, Python, Go, or Java — or when the user asks about auth flows, OAuth callbacks, token refresh, or session handling with Scalekit.
4integrating-agent-auth
Integrates Scalekit Agent Auth into a project to handle OAuth flows, token storage, and automatic refresh for third-party services (Gmail, Slack, Notion, Calendar). Use when a user needs to connect to an external service, authorize OAuth access, fetch access or refresh tokens, or execute API calls on behalf of a user.
4adding-mcp-oauth
Guides users through adding OAuth 2.1 authorization to Model Context Protocol (MCP) servers using Scalekit. Use when setting up MCP servers, implementing authentication for AI hosts like Claude Desktop, Cursor, or VS Code, or when users mention MCP security, OAuth, or Scalekit integration.
3modular-sso
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.
3sk-actions-custom-provider
Create or review Scalekit custom providers/connectors for proxy-only usage. Use this skill when the task is to gather API docs, infer whether a connector is OAuth, Basic, Bearer, or API Key, determine required tracked fields like domain or version, generate provider JSON, check for existing custom providers, show update diffs, run approved create or update curls, and print resolved delete curls.
3