
Spring Security Configuration
- 214 installs
- 105 repo stars
- Updated July 27, 2026
- amplicode/spring-skills
spring-security-configuration is an agent skill that generates Spring Security JWT authentication converter helper methods for OAuth2 resource servers.
About
spring-security-configuration is an agent skill for solo and indie builders shipping Java APIs who need correct OAuth2 JWT authority mapping without hand-rolling Spring Security boilerplate. It focuses on a single, well-scoped artifact: a package-private JwtAuthenticationConverter helper method that configures JwtGrantedAuthoritiesConverter with the right claims claim and ROLE_ prefix, then attaches it through filterChain via jwtAuthenticationConverter(). The skill encodes Amplicode-style generation rules so you do not accidentally promote the helper to a @Bean or emit converter code when generateConverter is false. Provider-aware variables keep Keycloak and Cognito deployments aligned with how groups and roles appear in tokens. Use it while implementing resource-server security in an existing Spring configuration class, after you have chosen an identity provider and know whether custom authority mapping is required.
- Package-private jwtAuthenticationConverter() helper—not a @Bean—wired via filterChain
- JwtGrantedAuthoritiesConverter with configurable claim name and ROLE_ prefix
- Provider-derived defaults: roles claim for KEYCLOAK, cognito:groups for AWS_COGNITO
- Converter block only generated when generateConverter=true and provider is KEYCLOAK or AWS_COGNITO
- Documents insert point inside the configuration class body
Spring Security Configuration by the numbers
- 214 all-time installs (skills.sh)
- Ranked #751 of 2,203 Security skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/amplicode/spring-skills --skill spring-security-configurationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 214 |
|---|---|
| repo stars | ★ 105 |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 27, 2026 |
| Repository | amplicode/spring-skills ↗ |
What it does
Generate Spring Security JWT authentication converter helper code for OAuth2 resource servers with Keycloak or AWS Cognito claim conventions.
Who is it for?
Best when you're on Spring Boot adding JWT login with Keycloak or AWS Cognito group/role claims.
Skip if: Greenfield projects that need full filter chains, method security, or providers outside KEYCLOAK and AWS_COGNITO converter generation paths.
When should I use this skill?
User is implementing or fixing Spring Security OAuth2 JWT resource-server configuration with Keycloak or AWS Cognito authority claims.
What you get
You get paste-ready Java for jwtAuthenticationConverter() with provider-appropriate claim names and ROLE_ mapping inside your security configuration class.
- jwtAuthenticationConverter() helper method with JwtGrantedAuthoritiesConverter settings
- Documented claimName variable mapping for the selected provider
By the numbers
- Helper is package-private and explicitly not a @Bean
- Default claim names: roles for KEYCLOAK and cognito:groups for AWS_COGNITO when converter generation is enabled
Files
Preflight: Spring MCP
This skill is part of the Spring Agent Toolkit and is designed to work with the Spring MCP server (provided by the Amplicode IntelliJ plugin). Before doing anything else, check your tool list for any Spring MCP tool — they are exposed under the amplicode MCP server (e.g. get_project_summary, list_module_dependencies, get_entity_details); harnesses that flatten MCP tools into the tool list use the mcp__amplicode__ prefix on the same names.
- If at least one Amplicode tool is available — MCP is connected. Proceed with the skill below.
- If none are available — stop and invoke the `amplicode-install` skill (bundled with the Spring Agent Toolkit). It installs the Amplicode plugin and walks the user through the «Настроить Spring Agent» welcome-screen button + MCP-client restart. After it completes, the MCP tools become available — resume this skill.
- If
amplicode-installis not registered in your skill list, tell the user (in their language): "This skill needs the Amplicode IntelliJ plugin and its MCP server. Install it from https://amplicode.ru/marketplace into IntelliJ IDEA Ultimate/Community or GigaIDE, open any project, click «Настроить Spring Agent» on the Amplicode welcome screen, then restart your MCP client."
---
Spring Security Configuration
Generates a @Configuration @EnableWebSecurity class with a filterChain() method, DSL chains for authentication and authorization, beans, dependencies, and properties in application.properties.
---
CRITICAL: Code ONLY from examples/ files. If no matching example -- STOP and ask user.
CRITICAL: For questions with a fixed set of choices, prefer `AskUserQuestion` > its analogue > plain text list. Plain numbered text lists are the last resort when no interactive tool is available.
CRITICAL: Read the conversation context BEFORE running Step 1. Half the questions in Steps 2–3 may already be answered by the user's prompt and prior turns. Re-asking what was already said is the #1 reason this skill feels slow.
---
Defaults
| Option | Default | Always ask? | Notes |
|---|---|---|---|
| Authentication type | — | YES | main branching |
| Disable CSRF | no | NO | |
| Disable headers | no | NO | |
| Disable anonymous access | no | NO | |
| Authorization rules | anyRequest().authenticated() | NO | |
| language | from get_project_summary | NO | auto-detected |
| bootVersion | from get_project_summary | NO | auto-detected |
| className | SecurityConfiguration | NO | suggest, confirm only |
| packageName | same as existing @Configuration | NO | auto-detected |
| appPrefix | from existing @Value properties or app | NO | auto-detected from project |
Smart defaults: If user says "use defaults", "all defaults", "default settings", or similar -- skip ALL questions where "Always ask?" = NO. Only ask mandatory questions.
Smart answer recognition: When user provides a value instead of choosing from a numbered list, accept it directly. Examples:
- Question "Authentication type?" -> user answers "jwt" -> this IS the choice, don't show options
- Question "Client ID?" -> user answers "my-app" -> this IS the value, don't re-ask
- If user provides multiple answers in one message -> accept all, skip answered questions
- NEVER ask a question that the user already answered (even implicitly)
Batch questions: Group closely related questions into a single AskUserQuestion call (up to 4 questions per call) when they:
- Belong to the same logical section (e.g. both are connection settings)
- Don't depend on each other's answers
- Have obvious defaults that the user can skip
Rules:
- Maximum 3-4 questions per
AskUserQuestioncall - Mark the recommended option with
(Recommended)and place it first - Never batch questions from DIFFERENT decision branches
- The primary branching question (authentication type) is always asked ALONE
- Prefer
AskUserQuestionfor choices; fall back to plain text lists only if the tool is unavailable
---
Decision-making principle — context first, then ask
Before asking the user any question, attempt to derive the answer from the context already gathered: project summary, module dependencies, existing security configurations, prior turns of this conversation, and the user's original prompt. Only ask when the context yields no clear default or when the choice is genuinely user-specific (e.g. authentication type, authorization rules).
Hierarchy of decisions:
1. Context is unambiguous → decide silently, do NOT ask. Examples: language and Boot version from get_project_summary; existing dependencies from list_module_dependencies; packageName from existing @Configuration classes; className = SecurityConfiguration; appPrefix from existing @Value annotations.
2. Context gives a strong signal → state the decision + alternatives in one line, let the user override or stay silent. Format:
Will create SecurityConfiguration with JWT (spring-boot-starter-oauth2-resource-server found in dependencies).
Alternatives: Form Login, OIDC, LDAP, Custom. OK?The user can answer "ok" / "yes" / silence → accept; or name an alternative → switch.
3. Context yields no clear default → ask with `AskUserQuestion` (preferred), with the recommended option first. Mark the recommended option with (Recommended) in its label and place it first. If no interactive choice tool is available, fall back to a plain text list.
4. Context is fully empty for a critical input → ask plainly. This applies to: authentication type (when no deps hint at it), authorization rules, variant-specific settings.
How to ask — prefer AskUserQuestion
When a question must be asked, prefer the `AskUserQuestion` tool over writing a numbered list in the response body. Fall back to plain text only if no interactive choice tool is available.
Rules for AskUserQuestion calls in this skill:
- Each call may contain up to 4 questions that are independent of each
other. Use this to batch related decisions in one round-trip.
- Each question has 2–4 options. The tool auto-adds an "Other" choice
for free-form input — never include it manually.
- Mark the recommended option by putting it first with
(Recommended)
appended to the label.
headeris a 12-char chip label (e.g. "Auth Type", "CSRF", "Headers").
When AskUserQuestion is not the right tool:
- Free-form input with no enumerable set of options (e.g. URL, client ID,
DN, patterns) — ask in plain text.
- The "single confirmation line" from principle 2 — that is a plain
yes/no, not an enumerated choice.
NEVER ask the user for credentials of any kind (client secret, manager password, API key, token, etc.) — not via AskUserQuestion, not in plain text, not in any form. Secret values must not enter the conversation. For every credential field, the skill emits an env-var placeholder (${OAUTH_*_CLIENT_SECRET}, ${LDAP_MANAGER_PASSWORD}, ${AUTHSERVER_*_CLIENT_SECRET}) into application.properties and reports the placeholder name to the user after generation — see the per-variant references and _properties/*/properties.md "secret handling" sections.
The question lists in Steps 2–3 and in reference files are a fallback for case 4. They are NOT a script to execute top-to-bottom. If a question's answer is already determined by principles 1–3, skip the question.
---
Step 0 -- Conversation context first (REQUIRED, no tool calls)
Before any MCP call, before any question, re-read the user's prompt and the prior turns of this conversation and extract whatever is already stated. This step costs nothing and prevents the most common failure mode of this skill — asking the user something they already said.
Build a mental checklist of inputs and tick off everything the user has already provided, explicitly or implicitly:
| Input | Look for in the prompt / context |
|---|---|
| authentication type | "JWT", "form login", "OIDC", "Keycloak", "LDAP", "OAuth2", "authorization server" — any direct or implied mention |
| provider | "Keycloak", "Google", "GitHub", "Okta", "AWS Cognito" — implies OIDC or JWT variant |
| authorization rules | "only for ADMIN", "public API", "all endpoints secured" |
| language | Kotlin / Java — also implied by file extensions in discussion |
| className | "name it SecurityConfig", "class name XxxConfiguration" |
| smart defaults | "use defaults", "all defaults", "default settings", "minimal configuration" |
| prior project facts | language, Boot version, dependencies — already known if discussed earlier in this conversation; do not re-fetch |
For every input that is explicitly or strongly implicitly answered: mark it as decided and skip the corresponding question in Steps 2–3. Do NOT ask "Authentication type?" if the user wrote "configure JWT" — JWT is the answer. Do NOT ask language if the user wrote "in Kotlin".
For every input that is not answered: defer to the Decision-making principle above — try to derive it from project context first (Step 1), and only then ask.
Step 0 is mental, not a tool call. Do not announce it to the user. Do not write "Step 0 done". Just internalize what the user already said before proceeding to Step 1.
---
Step 1 -- Gather context (automatic, no questions)
Call Spring MCP tools (in parallel where possible).
| Tool | What to extract | Variable name |
|---|---|---|
get_project_summary | language, springBootVersion, moduleName, buildFile, mainPackage | language, bootMajor, moduleName, buildFile, mainPackage |
list_module_dependencies(moduleName) | artifact IDs | presentDeps |
list_application_properties_files(moduleName) | path to properties file | propsFile |
list_security_configurations | existing security configs | existingConfigs |
list_spring_security_roles | existing roles for authorize rules | existingRoles |
If multi-module project (multiple modules in get_project_summary): Ask which module to use. Then re-call module-specific MCP tools with that module.
If existingConfigs is not empty:
- Warn user: "Project already has security configuration(s): {list}. Create an additional one?"
- If user confirms — suggest a
{className}that does not collide with existing names - If user declines — STOP
Determine appPrefix: scan existing @Value annotations in the project for a common custom prefix (e.g. @Value("${myapp.something}") → appPrefix = myapp). If none found, default to app.
---
Step 2 -- Authentication type
Ask:
Authentication type?
1. Form Login (HTTP Session) -- form login with session
2. JWT (OAuth2 Resource Server) -- stateless JWT
3. OAuth2/OIDC Login -- login via external provider (Keycloak, Google, GitHub...)
4. Authorization Server -- own authorization server (Spring Boot >= 3.1)
5. LDAP -- LDAP authentication
6. Custom -- empty filterChain, only common DSL blocksMap answer to reference file:
- 1 ->
references/http-session.md - 2 ->
references/jwt.md - 3 ->
references/oidc.md - 4 ->
references/authorization-server.md(requires bootMajor >= 3.1 — if lower, warn user and ask to choose another type) - 5 ->
references/ldap.md - 6 ->
references/custom.md
---
Step 3 -- Variant-specific questions (inline)
Read the mapped reference file and follow its questions flow. Only asked if user did NOT say "all defaults".
Each reference file specifies:
- Which fragments to include
- Which beans to generate
- Which dependencies to add
- Which properties to write
- Variant-specific questions
---
Step 4 -- Generate code
1. Read skeleton from examples/_skeletons/{lang}.md where {lang} = java or kotlin (from Step 1)
2. Create the configuration class file using the skeleton, substituting {packageName} and {className}
3. Read the reference file for the chosen variant. For each fragment listed:
- Read the fragment file from
examples/_fragments/{feature}/{lang}.md - Insert the appropriate code variant into the filterChain method body, before
return http.build(); - Select the correct version variant based on
bootMajorfrom Step 1 (some fragments have Boot < 3.1 / Boot >= 3.1 sections — use the matching one) - Apply variable substitutions (ONLY variables declared in the Variables section)
- Include ONLY the lines that match the user's chosen options (skip commented-out conditional lines)
4. DSL ordering inside filterChain method (IMPORTANT — order is VARIANT-DEPENDENT):
See references/common-dsl.md → "Generation Order" for the exact ordering per variant. Always end with return http.build();
5. For each bean listed in the reference:
- Read the bean file from
examples/_beans/{bean-type}/{lang}.md - Add the bean method to the configuration class body
- CRITICAL: Every bean method MUST be referenced from the filterChain DSL.
Do NOT create methods that are unused.
- If the bean requires autowired fields (e.g.
clientRegistrationRepository), add them to the class
6. Variable substitution rules:
{packageName}-> from Step 1 context{className}-> from user or default- Other variables -> from user answers or defaults
- NEVER substitute anything not listed in Variables
- NEVER add imports, methods, or code not in the example
- FQN only in imports, clean code in body. Examples may contain fully qualified names
inline (e.g. new org.springframework.security.web.SecurityFilterChain(...)). When generating code, extract FQNs into import statements and use short class names in the code body. The generated file must look like normal hand-written code.
---
Step 5 -- Dependencies & properties (automatic)
1. Read _dependencies/base.md (always) 2. Read variant-specific dependency file (from the reference file) 3. Select the correct version variant based on bootMajor from Step 1:
- Dependency files contain Boot 3.x / Boot 4.x sections — use the one matching
bootMajor - Boot 4.x changed OAuth2 artifact names:
spring-boot-starter-oauth2-*→spring-boot-starter-security-oauth2-* - Authorization Server on Boot 3.0: requires manual
spring-security-oauth2-authorization-server:1.1.0(no starter)
4. For each artifact NOT in presentDeps:
- Use
buildFilefrom Step 1 - Edit the build file to add the dependency
5. Call refresh_build_system_model 6. Read the variant-specific properties file from _properties/{variant}/properties.md 7. Write/append to the application properties file 8. Report: "Added dependencies: [list]. Wrote to application.properties: [keys]" 9. If the generated properties contain any ${...} env-var placeholders for credentials (OAUTH_*_CLIENT_SECRET, LDAP_MANAGER_PASSWORD, AUTHSERVER_*_CLIENT_SECRET), list every placeholder explicitly and instruct the user to set these env vars before running the app (shell export, IDE run configuration, or deployment secret store). Never ask the user for the secret value itself — secrets must not enter the conversation history.
---
Anti-hallucination checklist
Before writing ANY code, verify:
- [ ] The code comes from an examples/ file (cite which one)
- [ ] Only declared variables were substituted
- [ ] No framework API calls were added "from knowledge"
- [ ] Imports are derived from FQNs used in the example (no extra, no missing)
- [ ] Method signatures match the example exactly
- [ ] No comments or convenience methods were added
- [ ] FQNs from examples are extracted into imports; code body uses short class names only
- [ ] DSL ordering inside filterChain follows the VARIANT-SPECIFIC order defined in Step 4 (HTTP Session/JWT: authorizeHttpRequests first; OIDC: oauth2Login first, then logout, then authorizeHttpRequests)
- [ ] Helper methods (jwtAuthenticationConverter, oidcClientInitiatedLogoutSuccessHandler) are NOT @Bean — they are public methods called directly from filterChain. Note: userAuthoritiesMapper is @Bean for generic providers, but NOT @Bean for Keycloak (see role-mapper bean file)
- [ ] Every bean method generated is actually referenced from the filterChain DSL or from another bean — no orphan methods
- [ ] For HTTP Session: if User Storage is configured,
http.userDetailsService(...)call is present in filterChain - [ ] For HTTP Session: logout DSL is only generated when non-default logout options are set (Spring Security enables default logout automatically)
- [ ] Version-specific code/dependencies match
bootMajor: Boot 4.x usesspring-boot-starter-security-oauth2-*(notspring-boot-starter-oauth2-*); session-management concurrent sessions API changed in Boot 3.1
JWT Authentication Converter helper method (Java)
Insert Point
As a package-private helper method in the configuration class body. NOT a @Bean — called directly from filterChain via .jwtAuthenticationConverter(jwtAuthenticationConverter()).
Code
// defaults: not generated (generateConverter=false)
// Only generated when generateConverter=true AND provider is KEYCLOAK or AWS_COGNITO
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter jwtAuthenticationConverter() {
org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthoritiesClaimName("{claimName}");
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter jwtAuthenticationConverter = new org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}Variables
| Variable | Source | Default |
|---|---|---|
{claimName} | derived from provider | "roles" for KEYCLOAK, "cognito:groups" for AWS_COGNITO |
JWT Authentication Converter helper method (Kotlin)
Insert Point
As a private/internal helper method in the configuration class body. NOT a @Bean — called directly from filterChain via .jwtAuthenticationConverter(jwtAuthenticationConverter()).
Code
// defaults: not generated (generateConverter=false)
// Only generated when generateConverter=true AND provider is KEYCLOAK or AWS_COGNITO
// NOT @Bean — helper method called directly from filterChain
fun jwtAuthenticationConverter(): org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter {
val grantedAuthoritiesConverter: org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter = org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter()
grantedAuthoritiesConverter.setAuthoritiesClaimName("{claimName}")
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_")
val jwtAuthenticationConverter: org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter = org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter()
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter)
return jwtAuthenticationConverter
}Variables
| Variable | Source | Default |
|---|---|---|
{claimName} | derived from provider | "roles" for KEYCLOAK, "cognito:groups" for AWS_COGNITO |
LDAP Authentication Manager bean (Java)
Insert Point
As @Bean method in the configuration class body.
Code
// defaults: BIND authentication, NO_AUTHORITIES
// BIND authentication type:
@org.springframework.context.annotation.Bean
org.springframework.security.authentication.AuthenticationManager ldapAuthenticationManager() {
org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory factory = new org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory(contextSource());
factory.setUserDnPatterns({ldapUserDnPatternsField});
factory.setLdapAuthoritiesPopulator({ldapAuthoritiesPopulatorRef}); // if authorities populator is set
factory.setAuthoritiesMapper({authoritiesMapperRef}); // if authoritiesMapper is set
factory.setUserDetailsContextMapper({userDetailsContextMapperRef}); // if userDetailsContextMapper is set
return factory.createAuthenticationManager();
}
// PASSWORD authentication type:
@org.springframework.context.annotation.Bean
org.springframework.security.authentication.AuthenticationManager ldapAuthenticationManager() {
org.springframework.security.config.ldap.LdapPasswordComparisonAuthenticationManagerFactory factory = new org.springframework.security.config.ldap.LdapPasswordComparisonAuthenticationManagerFactory(contextSource(), {passwordEncoderRef});
factory.setUserDnPatterns({ldapUserDnPatternsField});
factory.setLdapAuthoritiesPopulator({ldapAuthoritiesPopulatorRef});
return factory.createAuthenticationManager();
}Authorities populator variants (bean body):
// NO_AUTHORITIES:
return new org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator();
// GROUP:
return new org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator(contextSource(), {ldapGroupSearchBaseField});
// GROUP_WITH_HIERARCHY:
return new org.springframework.security.ldap.userdetails.NestedLdapAuthoritiesPopulator(contextSource(), {ldapGroupSearchBaseField});
// CUSTOM:
return (userData, username) -> {
//TODO: add granted authorities
return java.util.Collections.emptyList();
};Variables
| Variable | Source | Default |
|---|---|---|
{ldapUserDnPatternsField} | @Value field, property key: {appPrefix}.ldap.userDnPatterns | — |
{ldapGroupSearchBaseField} | @Value field, property key: {appPrefix}.ldap.groupSearchBase | only for GROUP/GROUP_WITH_HIERARCHY |
{passwordEncoderRef} | bean reference | only for PASSWORD type |
{ldapAuthoritiesPopulatorRef} | bean method reference | depends on authoritiesSelector |
{authoritiesMapperRef} | bean reference | skip if not set |
{userDetailsContextMapperRef} | bean reference | skip if not set |
LDAP Authentication Manager bean (Kotlin)
Insert Point
As @Bean method in the configuration class body.
Code
// defaults: BIND authentication, NO_AUTHORITIES
// BIND authentication type:
@org.springframework.context.annotation.Bean
fun ldapAuthenticationManager(): org.springframework.security.authentication.AuthenticationManager {
val factory: org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory = org.springframework.security.config.ldap.LdapBindAuthenticationManagerFactory(contextSource())
factory.setUserDnPatterns({ldapUserDnPatternsField})
factory.setLdapAuthoritiesPopulator({ldapAuthoritiesPopulatorRef}) // if authorities populator is set
factory.setAuthoritiesMapper({authoritiesMapperRef}) // if authoritiesMapper is set
factory.setUserDetailsContextMapper({userDetailsContextMapperRef}) // if userDetailsContextMapper is set
return factory.createAuthenticationManager()
}
// PASSWORD authentication type:
@org.springframework.context.annotation.Bean
fun ldapAuthenticationManager(): org.springframework.security.authentication.AuthenticationManager {
val factory: org.springframework.security.config.ldap.LdapPasswordComparisonAuthenticationManagerFactory = org.springframework.security.config.ldap.LdapPasswordComparisonAuthenticationManagerFactory(contextSource(), {passwordEncoderRef})
factory.setUserDnPatterns({ldapUserDnPatternsField})
factory.setLdapAuthoritiesPopulator({ldapAuthoritiesPopulatorRef})
return factory.createAuthenticationManager()
}Authorities populator variants (bean body):
// NO_AUTHORITIES:
return org.springframework.security.ldap.authentication.NullLdapAuthoritiesPopulator()
// GROUP:
return org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator(contextSource(), {ldapGroupSearchBaseField})
// GROUP_WITH_HIERARCHY:
return org.springframework.security.ldap.userdetails.NestedLdapAuthoritiesPopulator(contextSource(), {ldapGroupSearchBaseField})
// CUSTOM:
return LdapAuthoritiesPopulator { userData, username ->
//TODO: add granted authorities
emptyList()
}Variables
| Variable | Source | Default |
|---|---|---|
{ldapUserDnPatternsField} | @Value field, property key: {appPrefix}.ldap.userDnPatterns | — |
{ldapGroupSearchBaseField} | @Value field, property key: {appPrefix}.ldap.groupSearchBase | only for GROUP/GROUP_WITH_HIERARCHY |
{passwordEncoderRef} | bean reference | only for PASSWORD type |
{ldapAuthoritiesPopulatorRef} | bean method reference | depends on authoritiesSelector |
{authoritiesMapperRef} | bean reference | skip if not set |
{userDetailsContextMapperRef} | bean reference | skip if not set |
LDAP Context Source bean (Java)
Insert Point
As @Bean method in the configuration class body.
Code
// defaults: anonymous access
// Two variants: anonymous and authenticated (with manager credentials)
// Anonymous access:
@org.springframework.context.annotation.Bean
org.springframework.ldap.core.support.BaseLdapPathContextSource contextSource() {
return new org.springframework.security.ldap.DefaultSpringSecurityContextSource({ldapUrlField});
}
// Authenticated access (with manager credentials):
@org.springframework.context.annotation.Bean
org.springframework.ldap.core.support.BaseLdapPathContextSource contextSource() {
org.springframework.security.ldap.DefaultSpringSecurityContextSource contextSource = new org.springframework.security.ldap.DefaultSpringSecurityContextSource({ldapUrlField});
contextSource.setUserDn({ldapManagerDnField});
contextSource.setPassword({ldapManagerPasswordField});
return contextSource;
}{ldapUrlField}, {ldapManagerDnField}, {ldapManagerPasswordField} are fields injected via @org.springframework.beans.factory.annotation.Value from application properties.
Variables
| Variable | Source | Default |
|---|---|---|
{ldapUrlField} | @Value field, property key: {appPrefix}.ldap.url | value: ldap(s)://{host}:{port}/{baseDn} |
{ldapManagerDnField} | @Value field, property key: {appPrefix}.ldap.managerDn | skip if anonymous access |
{ldapManagerPasswordField} | @Value field, property key: {appPrefix}.ldap.managerPassword | skip if anonymous access |
LDAP Context Source bean (Kotlin)
Insert Point
As @Bean method in the configuration class body.
Code
// defaults: anonymous access
// Anonymous access:
@org.springframework.context.annotation.Bean
fun contextSource(): org.springframework.ldap.core.support.BaseLdapPathContextSource =
org.springframework.security.ldap.DefaultSpringSecurityContextSource({ldapUrlField})
// Authenticated access (with manager credentials):
@org.springframework.context.annotation.Bean
fun contextSource(): org.springframework.ldap.core.support.BaseLdapPathContextSource {
val contextSource: org.springframework.security.ldap.DefaultSpringSecurityContextSource = org.springframework.security.ldap.DefaultSpringSecurityContextSource({ldapUrlField})
contextSource.setUserDn({ldapManagerDnField})
contextSource.setPassword({ldapManagerPasswordField})
return contextSource
}Variables
| Variable | Source | Default |
|---|---|---|
{ldapUrlField} | @Value field, property key: {appPrefix}.ldap.url | — |
{ldapManagerDnField} | @Value field | skip if anonymous access |
{ldapManagerPasswordField} | @Value field | skip if anonymous access |
OIDC Logout Success Handler helper method (Java)
Insert Point
As a package-private helper method in the configuration class body. NOT a @Bean — called directly from filterChain via http.logout(logout -> logout.logoutSuccessHandler(oidcClientInitiatedLogoutSuccessHandler())). Requires clientRegistrationRepository field injected via constructor.
Code
// defaults: always generated for OIDC_STATEFUL when isJwt=false
// Constructor-injected field (add to class):
private final org.springframework.security.oauth2.client.registration.ClientRegistrationRepository clientRegistrationRepository;
// Constructor:
public {className}(org.springframework.security.oauth2.client.registration.ClientRegistrationRepository clientRegistrationRepository) {
this.clientRegistrationRepository = clientRegistrationRepository;
}
// Helper method (NOT @Bean):
org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler oidcClientInitiatedLogoutSuccessHandler() {
org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler successHandler = new org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository);
successHandler.setPostLogoutRedirectUri("{postLogoutRedirectUri}");
return successHandler;
}Variables
| Variable | Source | Default |
|---|---|---|
{postLogoutRedirectUri} | user input | http://localhost:8080/ |
OIDC Logout Success Handler helper method (Kotlin)
Insert Point
As a private/internal helper method in the configuration class body. NOT a @Bean — called directly from filterChain via http.logout { it.logoutSuccessHandler(oidcClientInitiatedLogoutSuccessHandler()) }. Requires clientRegistrationRepository injected via constructor.
Code
// defaults: always generated for OIDC_STATEFUL when isJwt=false
// Constructor-injected field (add to class):
private val clientRegistrationRepository: org.springframework.security.oauth2.client.registration.ClientRegistrationRepository
// Constructor:
// class {className}(private val clientRegistrationRepository: ClientRegistrationRepository) {
// NOT @Bean — helper method called directly from filterChain
fun oidcClientInitiatedLogoutSuccessHandler(): org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler {
val successHandler: org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler = org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler(clientRegistrationRepository)
successHandler.setPostLogoutRedirectUri("{postLogoutRedirectUri}")
return successHandler
}Variables
| Variable | Source | Default |
|---|---|---|
{postLogoutRedirectUri} | user input | http://localhost:8080/ |
Role Mapper method (Java)
Insert Point
As a public method in the configuration class body.
Keycloak variant: NOT a @Bean — called directly from oauth2Login DSL via .userAuthoritiesMapper(userAuthoritiesMapper()). Generic variant: @Bean — annotated with @Bean, registered as a Spring bean (NOT called from DSL directly).
Code
// defaults: not generated unless OIDC_STATEFUL
// Two variants: generic (all providers) and Keycloak-specific
// Generic variant (with @Bean):
@org.springframework.context.annotation.Bean
public org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities) -> {
java.util.Set<org.springframework.security.core.GrantedAuthority> mappedAuthorities = new java.util.HashSet<>();
authorities.forEach(authority -> {
//TODO Map roles
// if (authority instanceof OidcUserAuthority){
// OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
// JSONArray keycloakRoles = (JSONArray) oidcUserAuthority.getAttributes().get("roles");
// keycloakRoles.forEach(role -> mappedAuthorities.add(new SimpleGrantedAuthority((String) role)));
// } else {
// mappedAuthorities.add(authority);
// }
});
return mappedAuthorities;
};
}
// Keycloak variant (no @Bean, called from DSL):
public org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper userAuthoritiesMapper() {
return authorities -> {
java.util.Set<org.springframework.security.core.GrantedAuthority> mappedAuthorities = new java.util.HashSet<>();
authorities.forEach( authority -> {
// TODO: Do not forget to enable "Add to userinfo" in Keycloak (Realm | Client scopes | roles | Mappers | client roles)
// if (!(authority instanceof OidcUserAuthority oidcUserAuthority)) {
// return;
// }
//
// // noinspection unchecked
// Optional.ofNullable(oidcUserAuthority.getAttributes().get("resource_access"))
// .map(ra -> ((Map<String, ?>) ra).get("{clientId}"))
// .map(sbLegacy -> ((Map<String, ?>) sbLegacy).get("roles"))
// .ifPresent(roles -> ((List<String>) roles).stream()
// .map(r -> new SimpleGrantedAuthority("ROLE_" + r))
// .forEach(mappedAuthorities::add));
});
return mappedAuthorities;
};
}Variables
| Variable | Source | Default |
|---|---|---|
{clientId} | from OAuth provider clientId | — (only for Keycloak variant) |
Role Mapper bean (Kotlin)
Insert Point
As a method in the configuration class body.
Keycloak variant: NOT a @Bean — called directly from oauth2Login DSL via .userAuthoritiesMapper(userAuthoritiesMapper()). Generic variant: @Bean — annotated with @Bean, registered as a Spring bean (NOT called from DSL directly).
Code
// defaults: not generated unless OIDC_STATEFUL
// Two variants: generic (all providers) and Keycloak-specific
// Generic variant (with @Bean):
@org.springframework.context.annotation.Bean
fun userAuthoritiesMapper(): org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper {
return org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper { authorities ->
authorities.flatMap { authority ->
TODO("Map authorities")
// if (authority is OidcUserAuthority){
// val keycloakRoles = authority.attributes?.get("roles") as JSONArray?
// keycloakRoles?.map { role -> SimpleGrantedAuthority(role as String?) }?: emptyList()
// } else {
// listOf(authority)
// }
}
}
}
// Keycloak variant (no @Bean, called from DSL):
fun userAuthoritiesMapper() = org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper { authorities ->
val mappedAuthorities = hashSetOf<org.springframework.security.core.GrantedAuthority>()
authorities.filterIsInstance<org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority>().forEach { authority ->
// TODO: Do not forget to enable "Add to userinfo" in Keycloak (Realm | Client scopes | roles | Mappers | client roles)
// // noinspection unchecked
// val resourceAccess = authority.attributes["resource_access"] ?: return@forEach
// val client = (resourceAccess as (Map<*, *>))["{clientId}"] ?: return@forEach
// val roles = (client as Map<*, *>)["roles"]
// (roles as List<*>)
// .map { r -> SimpleGrantedAuthority("ROLE_$r") }
// .forEach(mappedAuthorities::add)
}
mappedAuthorities
}Variables
| Variable | Source | Default |
|---|---|---|
{clientId} | from OAuth provider clientId | — (only for Keycloak variant) |
User Storage bean (Java)
Insert Point
As method in the configuration class body. Called from filterChain via http.userDetailsService(...).
Code
// IN_MEMORY variant (default for HTTP Session):
// NOTE: This is NOT a @Bean — it is a plain method called inline from filterChain.
// The filterChain must include: http.userDetailsService(inMemoryUserDetailsService());
public org.springframework.security.core.userdetails.UserDetailsService inMemoryUserDetailsService() {
org.springframework.security.core.userdetails.User.UserBuilder users = org.springframework.security.core.userdetails.User.builder();
org.springframework.security.provisioning.InMemoryUserDetailsManager userDetailsManager = new org.springframework.security.provisioning.InMemoryUserDetailsManager();
userDetailsManager.createUser(users.username("{username1}")
.password("{noop}{password1}")
.roles("{role1}")
.build());
// repeat for each user:
userDetailsManager.createUser(users.username("{username2}")
.password("{noop}{password2}")
.roles("{role2}")
.build());
return userDetailsManager;
}
// JDBC variant:
// Requires DataSource bean to be available in the context.
// The filterChain must include: http.userDetailsService(jdbcUserDetailsService());
public org.springframework.security.core.userdetails.UserDetailsService jdbcUserDetailsService() {
org.springframework.security.provisioning.JdbcUserDetailsManager userDetailsManager = new org.springframework.security.provisioning.JdbcUserDetailsManager({dataSourceField});
return userDetailsManager;
}
// JPA variant:
// Requires a custom UserDetailsService implementation.
// The user is asked for the bean reference of their UserDetailsService.
// The filterChain must include: http.userDetailsService({jpaUserDetailsServiceBean});
// No method is generated — the bean is assumed to exist.
// CUSTOM variant:
// The user is asked for the bean reference.
// The filterChain must include: http.userDetailsService({customUserDetailsServiceBean});
// No method is generated — the bean is assumed to exist.Variables
| Variable | Source | Default |
|---|---|---|
{username1}, {password1}, {role1} | from user table | admin / admin / ADMIN |
{username2}, {password2}, {role2} | from user table | user / user / USER |
{dataSourceField} | autowired DataSource field | dataSource |
{jpaUserDetailsServiceBean} | user input (bean ref) | — |
{customUserDetailsServiceBean} | user input (bean ref) | — |
Notes
- For IN_MEMORY: passwords are prefixed with
{noop}for plain-text encoding (development only) - For JDBC: needs
javax.sql.DataSourceautowired as constructor parameter - For JPA/CUSTOM: the user provides their own
UserDetailsServicebean - The
http.userDetailsService(...)call in filterChain is REQUIRED — without it, the UserDetailsService is not wired into the security chain
User Storage bean (Kotlin)
Insert Point
As method in the configuration class body. Called from filterChain via http.userDetailsService(...).
Code
// IN_MEMORY variant:
fun inMemoryUserDetailsService(): org.springframework.security.core.userdetails.UserDetailsService {
val users = org.springframework.security.core.userdetails.User.builder()
val userDetailsManager = org.springframework.security.provisioning.InMemoryUserDetailsManager()
userDetailsManager.createUser(users.username("{username1}")
.password("{noop}{password1}")
.roles("{role1}")
.build())
userDetailsManager.createUser(users.username("{username2}")
.password("{noop}{password2}")
.roles("{role2}")
.build())
return userDetailsManager
}
// JDBC variant:
fun jdbcUserDetailsService(): org.springframework.security.core.userdetails.UserDetailsService {
return org.springframework.security.provisioning.JdbcUserDetailsManager({dataSourceField})
}
// JPA variant:
// No method generated — the bean is assumed to exist.
// The filterChain must include: http.userDetailsService({jpaUserDetailsServiceBean})
// CUSTOM variant:
// No method generated — the bean is assumed to exist.
// The filterChain must include: http.userDetailsService({customUserDetailsServiceBean})Variables
Same as Java variant.
Authorization Server Dependencies (OAUTH_AUTHORIZATION_SERVER)
When to add
OAUTH_AUTHORIZATION_SERVER authentication type (Boot >= 3.1).
Dependencies
Boot 3.0 (without starter)
groupId: org.springframework.security
artifactId: spring-security-oauth2-authorization-server
version: 1.1.0Boot 3.1+
groupId: org.springframework.boot
artifactId: spring-boot-starter-oauth2-authorization-serverBoot 4.x
groupId: org.springframework.boot
artifactId: spring-boot-starter-security-oauth2-authorization-serverBase Dependencies (always)
| Artifact ID | Group ID | Scope | Condition |
|---|---|---|---|
| spring-boot-starter-security | org.springframework.boot | implementation | always |
Gradle Kotlin DSL
implementation("org.springframework.boot:spring-boot-starter-security")Gradle Groovy
implementation 'org.springframework.boot:spring-boot-starter-security'Maven
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>LDAP Dependencies (LDAP_STATEFUL)
| Artifact ID | Group ID | Scope | Condition |
|---|---|---|---|
| spring-security-ldap | org.springframework.security | implementation | LDAP_STATEFUL |
Gradle Kotlin DSL
implementation("org.springframework.security:spring-security-ldap")Gradle Groovy
implementation 'org.springframework.security:spring-security-ldap'Maven
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-ldap</artifactId>
</dependency>OAuth2 Client Dependencies (OIDC_STATEFUL)
When to add
OIDC_STATEFUL authentication type.
Dependencies
Boot 3.x
groupId: org.springframework.boot
artifactId: spring-boot-starter-oauth2-clientBoot 4.x
groupId: org.springframework.boot
artifactId: spring-boot-starter-security-oauth2-clientOAuth2 Resource Server dependency
When to add
JWT_STATELESS authentication type.
Dependencies
Boot 3.x
groupId: org.springframework.boot
artifactId: spring-boot-starter-oauth2-resource-serverBoot 4.x
groupId: org.springframework.boot
artifactId: spring-boot-starter-security-oauth2-resource-serverAnonymous DSL fragment
Insert Point
Inside filterChain method body, before return http.build();
Code
// defaults: enabled, no options
// When all options are default — skip this fragment entirely
// When disabled=true:
http.anonymous(anonymous -> anonymous.disable());
// With options:
http.anonymous(anonymous -> anonymous
.key("{keyValue}") // if key is not blank
.authorities("{auth1}", "{auth2}") // if authorities is not empty
.authenticationProvider({providerBean}) // if authenticationProvider is set
.authenticationFilter({filterBean}) // if authenticationFilter is set
);Variables
| Variable | Source | Default |
|---|---|---|
{keyValue} | user input | skip if empty |
{auth1}, {auth2} | user input | skip if empty |
{providerBean} | user input (bean ref) | skip if not set |
{filterBean} | user input (bean ref) | skip if not set |
Anonymous DSL fragment (Kotlin)
Insert Point
Inside filterChain method body, before return http.build()
Code
// defaults: enabled, no options
// When all options are default — skip this fragment entirely
// When disabled=true:
http.anonymous { it.disable() }
// With options:
http.anonymous { anonymous ->
anonymous
.key("{keyValue}") // if key is not blank
.authorities("{auth1}", "{auth2}") // if authorities is not empty
.authenticationProvider({providerBean}) // if authenticationProvider is set
.authenticationFilter({filterBean}) // if authenticationFilter is set
}Variables
| Variable | Source | Default |
|---|---|---|
{keyValue} | user input | skip if empty |
{auth1}, {auth2} | user input | skip if empty |
{providerBean} | user input (bean ref) | skip if not set |
{filterBean} | user input (bean ref) | skip if not set |
Authorize HTTP Requests DSL fragment (Java)
Insert Point
Inside filterChain method body. Position depends on variant:
- HTTP Session / JWT / Custom: FIRST in the DSL chain
- OAuth2/OIDC: AFTER
oauth2LoginandlogoutDSL blocks
Code
// defaults: anyRequest().authenticated()
// Default output:
http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
.anyRequest().authenticated()
);
// With securityMatcher:
http.securityMatcher("{securityMatcher}");
http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
.requestMatchers("{pattern1}", "{pattern2}").hasRole("{role}")
.requestMatchers(org.springframework.http.HttpMethod.{METHOD}, "{pattern}").permitAll()
.anyRequest().authenticated()
);Permission methods available:
.permitAll()— allow all.authenticated()— require authentication.hasRole("{role}")— single role (without ROLE_ prefix).hasAnyRole("{role1}", "{role2}")— multiple roles.hasAuthority("{authority}")— single authority.hasAnyAuthority("{a1}", "{a2}")— multiple authorities.denyAll()— deny all.anonymous()— anonymous only.rememberMe()— remember-me only.fullyAuthenticated()— fully authenticated only
Matcher types:
.requestMatchers("{pattern}")— Boot 3+ (default).requestMatchers(org.springframework.http.HttpMethod.{METHOD}, "{pattern}")— with HTTP method.dispatcherTypeMatchers(jakarta.servlet.DispatcherType.{TYPE})— dispatcher type matcher (FORWARD, INCLUDE, REQUEST, ASYNC, ERROR)
Variables
| Variable | Source | Default |
|---|---|---|
{securityMatcher} | user input | skip if empty |
{pattern1}, {pattern2}, {pattern} | user input | — |
{role}, {role1}, {role2} | user input or from list_spring_security_roles | — |
{authority}, {a1}, {a2} | user input | — |
{METHOD} | user choice | GET, POST, PUT, DELETE, etc. |
{TYPE} | user choice | FORWARD, INCLUDE, REQUEST, ASYNC, ERROR |
Authorize HTTP Requests DSL fragment (Kotlin)
Insert Point
Inside filterChain method body. Position depends on variant:
- HTTP Session / JWT / Custom: FIRST in the DSL chain
- OAuth2/OIDC: AFTER
oauth2LoginandlogoutDSL blocks
Code
// defaults: anyRequest().authenticated()
// Default output:
http.authorizeHttpRequests { authorizeHttpRequests ->
authorizeHttpRequests
.anyRequest().authenticated()
}
// With securityMatcher:
http.securityMatcher("{securityMatcher}")
http.authorizeHttpRequests { authorizeHttpRequests ->
authorizeHttpRequests
.requestMatchers("{pattern1}", "{pattern2}").hasRole("{role}")
.requestMatchers(org.springframework.http.HttpMethod.{METHOD}, "{pattern}").permitAll()
.anyRequest().authenticated()
}Variables
| Variable | Source | Default |
|---|---|---|
{securityMatcher} | user input | skip if empty |
{pattern1}, {pattern2}, {pattern} | user input | — |
{role}, {role1}, {role2} | user input or from list_spring_security_roles | — |
{authority}, {a1}, {a2} | user input | — |
{METHOD} | user choice | GET, POST, PUT, DELETE, etc. |
{TYPE} | user choice | FORWARD, INCLUDE, REQUEST, ASYNC, ERROR |
CSRF DSL fragment
Insert Point
Inside filterChain method body, before return http.build();
Code
// defaults: enabled, no matchers
// When all options are default — skip this fragment entirely
// When disabled=true:
http.csrf(csrf -> csrf.disable());
// Boot 3, with options:
http.csrf(csrf -> csrf
.ignoringRequestMatchers("{pattern1}", "{pattern2}") // if ignoringMatchers not empty
.csrfTokenRepository({csrfTokenRepositoryBean}) // if csrfTokenRepository is set
.requireCsrfProtectionMatcher({requireCsrfProtectionMatcherBean}) // if requireCsrfProtectionMatcher is set
.sessionAuthenticationStrategy({sessionAuthenticationStrategyBean}) // if sessionAuthenticationStrategy is set
);Variables
| Variable | Source | Default |
|---|---|---|
{pattern1}, {pattern2} | user input | — |
{csrfTokenRepositoryBean} | user input (bean ref) | skip if not set |
{requireCsrfProtectionMatcherBean} | user input (bean ref) | skip if not set |
{sessionAuthenticationStrategyBean} | user input (bean ref) | skip if not set |
CSRF DSL fragment (Kotlin)
Insert Point
Inside filterChain method body, before return http.build()
Code
// defaults: enabled, no matchers
// When all options are default — skip this fragment entirely
// When disabled=true:
http.csrf { it.disable() }
// With options:
http.csrf { csrf ->
csrf
.ignoringRequestMatchers("{pattern1}", "{pattern2}") // if ignoringMatchers not empty
.csrfTokenRepository({csrfTokenRepositoryBean}) // if csrfTokenRepository is set
.requireCsrfProtectionMatcher({requireCsrfProtectionMatcherBean}) // if requireCsrfProtectionMatcher is set
.sessionAuthenticationStrategy({sessionAuthenticationStrategyBean}) // if sessionAuthenticationStrategy is set
}Variables
| Variable | Source | Default |
|---|---|---|
{pattern1}, {pattern2} | user input | — |
{csrfTokenRepositoryBean} | user input (bean ref) | skip if not set |
{requireCsrfProtectionMatcherBean} | user input (bean ref) | skip if not set |
{sessionAuthenticationStrategyBean} | user input (bean ref) | skip if not set |
Exception Handling (Access Denied) DSL fragment (Java)
Insert Point
Inside filterChain method body, before return http.build();
Code
// defaults (when Access Denied Handling is enabled but no custom settings):
http.exceptionHandling(Customizer.withDefaults());
// When disabled=true:
http.exceptionHandling(exceptionHandling -> exceptionHandling.disable());
// With accessDeniedPage:
http.exceptionHandling(exceptionHandling -> exceptionHandling
.accessDeniedPage("{accessDeniedPage}") // if accessDeniedPage is not blank
);
// With accessDeniedHandler bean:
http.exceptionHandling(exceptionHandling -> exceptionHandling
.accessDeniedHandler({accessDeniedHandlerBean}) // if accessDeniedHandler bean is set
);
// With both accessDeniedHandler and authenticationEntryPoint (bean):
http.exceptionHandling(exceptionHandling -> exceptionHandling
.accessDeniedHandler({accessDeniedHandlerBean}) // if accessDeniedHandler bean is set
.authenticationEntryPoint({authenticationEntryPointBean}) // if authenticationEntryPoint bean is set
);
// With authenticationEntryPoint as HTTP status (HttpStatusEntryPoint):
http.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(new org.springframework.security.web.authentication.HttpStatusEntryPoint(org.springframework.http.HttpStatus.{ENTRY_POINT_STATUS})) // if authenticationEntryPoint HTTP status is set
);Variables
| Variable | Source | Default |
|---|---|---|
{accessDeniedPage} | user input | skip if empty |
{accessDeniedHandlerBean} | user input (bean ref) | skip if not set |
{authenticationEntryPointBean} | user input (bean ref) | skip if not set |
{ENTRY_POINT_STATUS} | user choice (HTTP status) | skip if not set (e.g., UNAUTHORIZED, FORBIDDEN) |
Exception Handling (Access Denied) DSL fragment (Kotlin)
Insert Point
Inside filterChain method body, before return http.build()
Code
// defaults (when Access Denied Handling is enabled but no custom settings):
http.exceptionHandling { }
// When disabled=true:
http.exceptionHandling { it.disable() }
// With accessDeniedPage:
http.exceptionHandling { exceptionHandling ->
exceptionHandling.accessDeniedPage("{accessDeniedPage}")
}
// With accessDeniedHandler bean:
http.exceptionHandling { exceptionHandling ->
exceptionHandling.accessDeniedHandler({accessDeniedHandlerBean})
}
// With both accessDeniedHandler and authenticationEntryPoint (bean):
http.exceptionHandling { exceptionHandling ->
exceptionHandling.accessDeniedHandler({accessDeniedHandlerBean})
exceptionHandling.authenticationEntryPoint({authenticationEntryPointBean})
}
// With authenticationEntryPoint as HTTP status:
http.exceptionHandling { exceptionHandling ->
exceptionHandling.authenticationEntryPoint(org.springframework.security.web.authentication.HttpStatusEntryPoint(org.springframework.http.HttpStatus.{ENTRY_POINT_STATUS}))
}Variables
Same as Java variant.
FormLogin DSL fragment (Java)
Insert Point
Inside filterChain method body, before return http.build();
Code
// defaults: enabled, handlerType=STATUS, no URLs set
// Default output (no customization):
http.formLogin(org.springframework.security.config.Customizer.withDefaults());
// When disabled=true:
http.formLogin(formLogin -> formLogin.disable());
// With STATUS handlers:
http.formLogin(formLogin -> formLogin
.loginPage("{loginUrl}") // if loginUrl is not blank
.usernameParameter("{usernameParam}") // if usernameParameter is not blank
.passwordParameter("{passwordParam}") // if passwordParameter is not blank
.failureUrl("{failureUrl}") // if failureUrl is not blank
.defaultSuccessUrl("{defaultSuccessUrl}") // if defaultSuccessUrl is not blank
.loginProcessingUrl("{loginProcessingUrl}") // if loginProcessingUrl is not blank
.failureForwardUrl("{failureForwardUrl}") // if failureForwardUrl is not blank
.successForwardUrl("{successForwardUrl}") // if successForwardUrl is not blank
.authenticationDetailsSource({authDetailsSourceBean}) // if authenticationDetailsSource bean is set
.successHandler((request, response, authentication) -> response.setStatus(org.springframework.http.HttpStatus.{successStatus}.value())) // if successHandlerStatus is set
.failureHandler((request, response, exception) -> response.setStatus(org.springframework.http.HttpStatus.{failureStatus}.value())) // if failureHandlerStatus is set
.permitAll() // if loginUrl or loginProcessingUrl is not empty
);
// With BEAN handlers:
http.formLogin(formLogin -> formLogin
.successHandler({successHandlerBean}) // if successHandler bean is set
.failureHandler({failureHandlerBean}) // if failureHandler bean is set
.permitAll()
);Variables
| Variable | Source | Default |
|---|---|---|
{loginUrl} | user input | skip if empty |
{usernameParam} | user input | skip if empty |
{passwordParam} | user input | skip if empty |
{failureUrl} | user input | skip if empty |
{defaultSuccessUrl} | user input | skip if empty |
{loginProcessingUrl} | user input | skip if empty |
{failureForwardUrl} | user input | skip if empty |
{successForwardUrl} | user input | skip if empty |
{successStatus} | user choice | skip if not set (e.g. OK) |
{failureStatus} | user choice | skip if not set (e.g. UNAUTHORIZED) |
{authDetailsSourceBean} | user input (bean ref) | skip if not set |
{successHandlerBean} | user input (bean ref) | skip if not set |
{failureHandlerBean} | user input (bean ref) | skip if not set |
FormLogin DSL fragment (Kotlin)
Insert Point
Inside filterChain method body, before return http.build()
Code
// defaults: enabled, handlerType=STATUS, no URLs set
// Default output (no customization):
http.formLogin(org.springframework.security.config.Customizer.withDefaults())
// When disabled=true:
http.formLogin { it.disable() }
// With STATUS handlers:
http.formLogin { formLogin ->
formLogin
.loginPage("{loginUrl}")
.usernameParameter("{usernameParam}")
.passwordParameter("{passwordParam}")
.failureUrl("{failureUrl}")
.defaultSuccessUrl("{defaultSuccessUrl}")
.loginProcessingUrl("{loginProcessingUrl}")
.failureForwardUrl("{failureForwardUrl}")
.successForwardUrl("{successForwardUrl}")
.authenticationDetailsSource({authDetailsSourceBean}) // if bean is set
.successHandler { _, response, _ -> response.status = org.springframework.http.HttpStatus.{successStatus}.value() }
.failureHandler { _, response, _ -> response.status = org.springframework.http.HttpStatus.{failureStatus}.value() }
.permitAll()
}
// With BEAN handlers:
http.formLogin { formLogin ->
formLogin
.successHandler({successHandlerBean})
.failureHandler({failureHandlerBean})
.permitAll()
}Variables
| Variable | Source | Default |
|---|---|---|
{loginUrl} | user input | skip if empty |
{usernameParam} | user input | skip if empty |
{passwordParam} | user input | skip if empty |
{failureUrl} | user input | skip if empty |
{defaultSuccessUrl} | user input | skip if empty |
{loginProcessingUrl} | user input | skip if empty |
{failureForwardUrl} | user input | skip if empty |
{successForwardUrl} | user input | skip if empty |
{successStatus} | user choice | skip if not set |
{failureStatus} | user choice | skip if not set |
{authDetailsSourceBean} | user input (bean ref) | skip if not set |
{successHandlerBean} | user input (bean ref) | skip if not set |
{failureHandlerBean} | user input (bean ref) | skip if not set |
Headers DSL fragment
Insert Point
Inside filterChain method body, before return http.build();
Code
// defaults: enabled, not disabled, all defaults active
// When all options are default — skip this fragment entirely (Spring Security defaults apply)
// When disabled=true:
http.headers(headers -> headers.disable());
// When disableDefaults=true (selective headers):
http.headers(headers -> headers
.defaultsDisabled()
.contentTypeOptions(org.springframework.security.config.Customizer.withDefaults()) // if contentTypeOptions=true
.xssProtection(org.springframework.security.config.Customizer.withDefaults()) // if xssProtection=true
.cacheControl(org.springframework.security.config.Customizer.withDefaults()) // if cacheControl=true
.httpStrictTransportSecurity(org.springframework.security.config.Customizer.withDefaults()) // if hsts=true
.frameOptions(frameOptions -> frameOptions.{frameOptionMethod}()) // if frameOptions != DISABLED
);
// Default mode (disableDefaults=false), with non-default options:
http.headers(headers -> headers
.frameOptions(frameOptions -> frameOptions.{frameOptionMethod}()) // if frameOptions is not DENY
.referrerPolicy(referrerPolicy -> referrerPolicy.policy(org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy.{POLICY_ENUM})) // if refererPolicy is not NO_REFERRER
.permissionsPolicy(permissionsPolicy -> permissionsPolicy.policy("{permissionPolicyValue}")) // if permissionPolicy is not blank
.addHeaderWriter({headerWriterBean}) // for each headerWriter bean reference (repeated)
);Variables
| Variable | Source | Default |
|---|---|---|
{frameOptionMethod} | user choice | deny (options: deny, sameOrigin, disabled) |
{POLICY_ENUM} | user choice | NO_REFERRER (options: NO_REFERRER, NO_REFERRER_WHEN_DOWNGRADE, SAME_ORIGIN, ORIGIN, STRICT_ORIGIN, ORIGIN_WHEN_CROSS_ORIGIN, STRICT_ORIGIN_WHEN_CROSS_ORIGIN, UNSAFE_URL) |
{permissionPolicyValue} | user input | empty (skip if empty) |
{headerWriterBean} | user input (bean ref) | skip if no headerWriters; repeated for each |
Headers DSL fragment (Kotlin)
Insert Point
Inside filterChain method body, before return http.build()
Code
// defaults: enabled, not disabled, all defaults active
// When all options are default — skip this fragment entirely
// When disabled=true:
http.headers { it.disable() }
// When disableDefaults=true (selective headers):
http.headers { headers ->
headers
.defaultsDisabled()
.contentTypeOptions(org.springframework.security.config.Customizer.withDefaults()) // if contentTypeOptions=true
.xssProtection(org.springframework.security.config.Customizer.withDefaults()) // if xssProtection=true
.cacheControl(org.springframework.security.config.Customizer.withDefaults()) // if cacheControl=true
.httpStrictTransportSecurity(org.springframework.security.config.Customizer.withDefaults()) // if hsts=true
.frameOptions { frameOptions -> frameOptions.{frameOptionMethod}() } // if frameOptions != DISABLED
}
// Default mode (disableDefaults=false), with non-default options:
http.headers { headers ->
headers
.frameOptions { frameOptions -> frameOptions.{frameOptionMethod}() } // if frameOptions is not DENY
.referrerPolicy { referrerPolicy -> referrerPolicy.policy(org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter.ReferrerPolicy.{POLICY_ENUM}) } // if refererPolicy is not NO_REFERRER
.permissionsPolicy { permissionsPolicy -> permissionsPolicy.policy("{permissionPolicyValue}") } // if permissionPolicy is not blank
.addHeaderWriter({headerWriterBean}) // for each headerWriter bean reference (repeated)
}Variables
| Variable | Source | Default |
|---|---|---|
{frameOptionMethod} | user choice | deny |
{POLICY_ENUM} | user choice | NO_REFERRER |
{permissionPolicyValue} | user input | empty (skip if empty) |
{headerWriterBean} | user input (bean ref) | skip if no headerWriters; repeated for each |
JWT Resource Server DSL fragment (Java)
Insert Point
Inside filterChain method body, before return http.build();
Code
// defaults: no customization
// Default output:
http.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer
.jwt(org.springframework.security.config.Customizer.withDefaults())
);
// With options:
http.oauth2ResourceServer(oauth2ResourceServer -> oauth2ResourceServer
.jwt(jwt -> jwt
.jwkSetUri({jwkSetUriField}) // if jwkSetUriType=CUSTOM (field injected via @Value)
.jwtAuthenticationConverter({converterBeanRef}) // if authenticationConverter is set OR generateConverter=true
.decoder({decoderBeanRef}) // if decoder is set
)
);Variables
| Variable | Source | Default |
|---|---|---|
{jwkSetUriField} | field name from @Value injection | skip if jwkSetUriType=SPRING |
{converterBeanRef} | bean reference | skip if not set |
{decoderBeanRef} | bean reference | skip if not set |
JWT Resource Server DSL fragment (Kotlin)
Insert Point
Inside filterChain method body, before return http.build()
Code
// defaults: no customization
// Default output:
http.oauth2ResourceServer { oauth2ResourceServer ->
oauth2ResourceServer.jwt(org.springframework.security.config.Customizer.withDefaults())
}
// With options:
http.oauth2ResourceServer { oauth2ResourceServer ->
oauth2ResourceServer.jwt { jwt ->
jwt
.jwkSetUri({jwkSetUriField})
.jwtAuthenticationConverter({converterBeanRef})
.decoder({decoderBeanRef})
}
}Variables
| Variable | Source | Default |
|---|---|---|
{jwkSetUriField} | field name from @Value injection | skip if jwkSetUriType=SPRING |
{converterBeanRef} | bean reference | skip if not set |
{decoderBeanRef} | bean reference | skip if not set |
Logout DSL fragment (Java)
Insert Point
Inside filterChain method body, before return http.build();
Code
// defaults: enabled by Spring Security automatically
// When all options are default — skip this fragment entirely (Spring Security provides default logout at /logout)
// When disabled=true:
http.logout(logout -> logout.disable());
// With options:
http.logout(logout -> logout
.logoutUrl("{logoutUrl}") // if logoutUrl is not blank (default: /logout)
.logoutRequestMatcher({logoutRequestMatcherBean}) // if logoutRequestMatcher bean is set (overrides logoutUrl)
.logoutSuccessUrl("{logoutSuccessUrl}") // if logoutSuccessUrl is not blank (default: /login?logout)
.clearAuthentication({clearAuthentication}) // if clearAuthentication is explicitly set (default: true)
.invalidateHttpSession({invalidateHttpSession}) // if invalidateHttpSession is explicitly set (default: true)
.deleteCookies("{cookie1}", "{cookie2}") // if deleteCookies is not empty
.logoutSuccessHandler({logoutSuccessHandlerBean}) // if logoutSuccessHandler bean is set (overrides logoutSuccessUrl)
.addLogoutHandler({logoutHandlerBean}) // for each additional logout handler bean (repeated)
.permitAll() // if logoutUrl is customized
);
// With STATUS handler (uses HttpStatusReturningLogoutSuccessHandler, NOT a lambda):
http.logout(logout -> logout
.logoutSuccessHandler(new org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler(org.springframework.http.HttpStatus.{logoutSuccessStatus})));
// OIDC variant (used with OAuth2/OIDC Login when isJwt=false):
http.logout(logout -> logout
.logoutSuccessHandler(oidcClientInitiatedLogoutSuccessHandler())
);Variables
| Variable | Source | Default |
|---|---|---|
{logoutUrl} | user input | skip if default (/logout) |
{logoutSuccessUrl} | user input | skip if default (/login?logout) |
{clearAuthentication} | user choice (boolean) | skip if default (true) |
{invalidateHttpSession} | user choice (boolean) | skip if default (true) |
{cookie1}, {cookie2} | user input (comma-separated) | skip if empty |
{logoutRequestMatcherBean} | user input (bean ref) | skip if not set; overrides logoutUrl |
{logoutSuccessHandlerBean} | user input (bean ref) | skip if not set |
{logoutSuccessStatus} | user choice (HTTP status) | skip if not set (e.g. OK) |
{logoutHandlerBean} | user input (bean ref) | skip if not set; repeated for each handler |
Logout DSL fragment (Kotlin)
Insert Point
Inside filterChain method body, before return http.build()
Code
// defaults: enabled by Spring Security automatically
// When all options are default — skip this fragment entirely
// When disabled=true:
http.logout { it.disable() }
// With options:
http.logout { logout ->
logout.logoutUrl("{logoutUrl}") // if logoutUrl is not blank
logout.logoutRequestMatcher({logoutRequestMatcherBean}) // if logoutRequestMatcher bean is set (overrides logoutUrl)
logout.logoutSuccessUrl("{logoutSuccessUrl}") // if logoutSuccessUrl is not blank
logout.clearAuthentication({clearAuthentication}) // if explicitly set
logout.invalidateHttpSession({invalidateHttpSession}) // if explicitly set
logout.deleteCookies("{cookie1}", "{cookie2}") // if deleteCookies is not empty
logout.logoutSuccessHandler({logoutSuccessHandlerBean}) // if bean is set
logout.addLogoutHandler({logoutHandlerBean}) // for each handler bean
logout.permitAll() // if logoutUrl is customized
}
// With STATUS handler (uses HttpStatusReturningLogoutSuccessHandler, NOT a lambda):
http.logout { logout ->
logout.logoutSuccessHandler(org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler(org.springframework.http.HttpStatus.{logoutSuccessStatus}))
}
// OIDC variant:
http.logout { logout ->
logout.logoutSuccessHandler(oidcClientInitiatedLogoutSuccessHandler())
}Variables
Same as Java variant.
OAuth2 Login DSL fragment (Java)
Insert Point
Inside filterChain method body, before return http.build();
Code
// defaults: oauth2Login with defaults, logout with OIDC handler
// Default output (no customization):
http.oauth2Login(org.springframework.security.config.Customizer.withDefaults());
http.logout(logout -> logout
.logoutSuccessHandler(oidcClientInitiatedLogoutSuccessHandler())
);
// When disabled=true:
http.oauth2Login(oauth2Login -> oauth2Login.disable());
// With full options:
http.oauth2Login(oauth2Login -> oauth2Login
.loginPage("{loginPage}") // if loginPage is not blank
.failureUrl("{failureUrl}") // if failureUrl is not blank
.loginProcessingUrl("{loginProcessingUrl}") // if loginProcessingUrl is not blank
.defaultSuccessUrl("{defaultSuccessUrl}") // if defaultSuccessUrl is not blank
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint
.baseUri({authorizationEndpointField}) // if authorizationEndpoint is not blank; extracted to @Value property
.authorizationRequestResolver({authorizationRequestResolverBean}) // if authorizationRequestResolver bean is set
.authorizationRequestRepository({authorizationRequestRepoBean}) // if authorizationRequestRepository bean is set
)
.redirectionEndpoint(redirectionEndpoint -> redirectionEndpoint
.baseUri({redirectionEndpointField}) // if redirectionEndpoint is not blank; extracted to @Value property
)
.authenticationDetailsSource({authDetailsSourceBean}) // if authenticationDetailsSource bean is set
.successHandler({successHandlerBean}) // if successHandler bean is set
.failureHandler({failureHandlerBean}) // if failureHandler bean is set
.clientRegistrationRepository({clientRegRepoBean}) // if clientRegistrationRepository bean is set
.authorizedClientRepository({authorizedClientRepoBean}) // if authorizedClientRepository bean is set
.authorizedClientService({authorizedClientServiceBean}) // if authorizedClientService bean is set
.tokenEndpoint(tokenEndpoint -> tokenEndpoint
.accessTokenResponseClient({accessTokenResponseClientBean}) // if accessTokenResponseClient bean is set
)
.userInfoEndpoint(userInfoEndpoint -> userInfoEndpoint
.userService({userServiceBean}) // if userService bean is set
.oidcUserService({oidcUserServiceBean}) // if oidcUserService bean is set
.userAuthoritiesMapper({userAuthoritiesMapperBean}) // if userAuthoritiesMapper bean is set (e.g. for KEYCLOAK role mapper)
)
);
// Logout (when !isJwt):
http.logout(logout -> logout
.logoutSuccessHandler(oidcClientInitiatedLogoutSuccessHandler())
);
// When isJwt=true (JWT-based OIDC, no logout handler):
// omit the http.logout() block entirelyVariables
| Variable | Source | Default |
|---|---|---|
{loginPage} | user input | skip if empty |
{failureUrl} | user input | skip if empty |
{loginProcessingUrl} | user input | skip if empty |
{defaultSuccessUrl} | user input | skip if empty |
{authorizationEndpointField} | extracted to @Value("${appPrefix}.oauth.authorization-endpoint") field | skip if empty |
{redirectionEndpointField} | extracted to @Value("${appPrefix}.oauth.redirection-endpoint") field | skip if empty |
{authDetailsSourceBean} | user input (bean ref) | skip if not set |
{successHandlerBean} | user input (bean ref) | skip if not set |
{failureHandlerBean} | user input (bean ref) | skip if not set |
{clientRegRepoBean} | user input (bean ref) | skip if not set |
{authorizedClientRepoBean} | user input (bean ref) | skip if not set |
{authorizedClientServiceBean} | user input (bean ref) | skip if not set |
{accessTokenResponseClientBean} | user input (bean ref) | skip if not set |
{authorizationRequestResolverBean} | user input (bean ref) | skip if not set |
{authorizationRequestRepoBean} | user input (bean ref) | skip if not set |
{userServiceBean} | user input (bean ref) | skip if not set |
{oidcUserServiceBean} | user input (bean ref) | skip if not set |
{userAuthoritiesMapperBean} | bean method reference | auto-generated for KEYCLOAK (see _beans/role-mapper/) |
oidcClientInitiatedLogoutSuccessHandler() | bean method reference | auto-generated bean (see _beans/logout-handler/) |
Notes
authorizationEndpointandredirectionEndpointvalues are extracted to application properties via@Valuefield injection (pattern:extractToApplicationProperty)authorizationEndpointproperty key:{appPrefix}.oauth.authorization-endpointredirectionEndpointproperty key:{appPrefix}.oauth.redirection-endpoint- Nested DSL blocks (
authorizationEndpoint,redirectionEndpoint,tokenEndpoint,userInfoEndpoint) are only emitted if at least one of their sub-options is set
OAuth2 Login DSL fragment (Kotlin)
Insert Point
Inside filterChain method body, before return http.build()
Code
// defaults: oauth2Login with defaults, logout with OIDC handler
// Default output (no customization):
http.oauth2Login(org.springframework.security.config.Customizer.withDefaults())
http.logout { it.logoutSuccessHandler(oidcClientInitiatedLogoutSuccessHandler()) }
// When disabled=true:
http.oauth2Login { it.disable() }
// With full options:
http.oauth2Login { oauth2Login ->
oauth2Login
.loginPage("{loginPage}") // if loginPage is not blank
.failureUrl("{failureUrl}") // if failureUrl is not blank
.loginProcessingUrl("{loginProcessingUrl}") // if loginProcessingUrl is not blank
.defaultSuccessUrl("{defaultSuccessUrl}") // if defaultSuccessUrl is not blank
.authorizationEndpoint { authorizationEndpoint ->
authorizationEndpoint
.baseUri({authorizationEndpointField}) // if authorizationEndpoint is not blank; extracted to @Value property
.authorizationRequestResolver({authorizationRequestResolverBean}) // if authorizationRequestResolver bean is set
.authorizationRequestRepository({authorizationRequestRepoBean}) // if authorizationRequestRepository bean is set
}
.redirectionEndpoint { redirectionEndpoint ->
redirectionEndpoint
.baseUri({redirectionEndpointField}) // if redirectionEndpoint is not blank; extracted to @Value property
}
.authenticationDetailsSource({authDetailsSourceBean}) // if authenticationDetailsSource bean is set
.successHandler({successHandlerBean}) // if successHandler bean is set
.failureHandler({failureHandlerBean}) // if failureHandler bean is set
.clientRegistrationRepository({clientRegRepoBean}) // if clientRegistrationRepository bean is set
.authorizedClientRepository({authorizedClientRepoBean}) // if authorizedClientRepository bean is set
.authorizedClientService({authorizedClientServiceBean}) // if authorizedClientService bean is set
.tokenEndpoint { tokenEndpoint ->
tokenEndpoint
.accessTokenResponseClient({accessTokenResponseClientBean}) // if accessTokenResponseClient bean is set
}
.userInfoEndpoint { userInfoEndpoint ->
userInfoEndpoint
.userService({userServiceBean}) // if userService bean is set
.oidcUserService({oidcUserServiceBean}) // if oidcUserService bean is set
.userAuthoritiesMapper({userAuthoritiesMapperBean}) // if userAuthoritiesMapper bean is set
}
}
// Logout (when !isJwt):
http.logout { it.logoutSuccessHandler(oidcClientInitiatedLogoutSuccessHandler()) }
// When isJwt=true (JWT-based OIDC, no logout handler):
// omit the http.logout() block entirelyVariables
| Variable | Source | Default |
|---|---|---|
{loginPage} | user input | skip if empty |
{failureUrl} | user input | skip if empty |
{loginProcessingUrl} | user input | skip if empty |
{defaultSuccessUrl} | user input | skip if empty |
{authorizationEndpointField} | extracted to @Value("${appPrefix}.oauth.authorization-endpoint") field | skip if empty |
{redirectionEndpointField} | extracted to @Value("${appPrefix}.oauth.redirection-endpoint") field | skip if empty |
{authDetailsSourceBean} | user input (bean ref) | skip if not set |
{successHandlerBean} | user input (bean ref) | skip if not set |
{failureHandlerBean} | user input (bean ref) | skip if not set |
{clientRegRepoBean} | user input (bean ref) | skip if not set |
{authorizedClientRepoBean} | user input (bean ref) | skip if not set |
{authorizedClientServiceBean} | user input (bean ref) | skip if not set |
{accessTokenResponseClientBean} | user input (bean ref) | skip if not set |
{authorizationRequestResolverBean} | user input (bean ref) | skip if not set |
{authorizationRequestRepoBean} | user input (bean ref) | skip if not set |
{userServiceBean} | user input (bean ref) | skip if not set |
{oidcUserServiceBean} | user input (bean ref) | skip if not set |
{userAuthoritiesMapperBean} | bean method reference | auto-generated for KEYCLOAK (see _beans/role-mapper/) |
oidcClientInitiatedLogoutSuccessHandler() | bean method reference | auto-generated bean (see _beans/logout-handler/) |
Notes
authorizationEndpointandredirectionEndpointvalues are extracted to application properties via@Valuefield injection- Nested DSL blocks are only emitted if at least one of their sub-options is set
Remember Me DSL fragment (Java)
Insert Point
Inside filterChain method body, before return http.build();
Code
// defaults: not included (Remember Me is disabled by default)
// Only generated when Remember Me is enabled
// Default (cookie-based):
http.rememberMe(org.springframework.security.config.Customizer.withDefaults());
// With options:
http.rememberMe(rememberMe -> rememberMe
.key("{rememberMeKey}") // if key is not blank
.tokenValiditySeconds({tokenValiditySeconds}) // if tokenValiditySeconds is set (default: 1209600 = 2 weeks)
.rememberMeParameter("{rememberMeParameter}") // if rememberMeParameter is not blank (default: "remember-me")
.rememberMeCookieName("{rememberMeCookieName}") // if rememberMeCookieName is not blank (default: "remember-me")
.useSecureCookie({useSecureCookie}) // if useSecureCookie is explicitly set
.userDetailsService({userDetailsServiceBean}) // if userDetailsService bean is set
.tokenRepository({tokenRepositoryBean}) // if tokenRepository bean is set (for persistent tokens)
.alwaysRemember({alwaysRemember}) // if alwaysRemember is explicitly set (default: false)
.rememberMeCookieDomain("{cookieDomain}") // if cookieDomain is not blank
.authenticationSuccessHandler({authSuccessHandlerBean}) // if authenticationSuccessHandler bean is set
.rememberMeServices({rememberMeServicesBean}) // if rememberMeServices bean is set (overrides default implementation)
);Variables
| Variable | Source | Default |
|---|---|---|
{rememberMeKey} | user input | skip if empty |
{tokenValiditySeconds} | user input (int) | skip if default (1209600) |
{rememberMeParameter} | user input | skip if default ("remember-me") |
{rememberMeCookieName} | user input | skip if default ("remember-me") |
{useSecureCookie} | user choice (boolean) | skip if not set |
{userDetailsServiceBean} | user input (bean ref) | skip if not set |
{tokenRepositoryBean} | user input (bean ref) | skip if not set |
{alwaysRemember} | user choice (boolean) | skip if default (false) |
{cookieDomain} | user input | skip if empty |
{authSuccessHandlerBean} | user input (bean ref) | skip if not set |
{rememberMeServicesBean} | user input (bean ref) | skip if not set; overrides default |
Remember Me DSL fragment (Kotlin)
Insert Point
Inside filterChain method body, before return http.build()
Code
// defaults: not included
// Default:
http.rememberMe { }
// With options:
http.rememberMe { rememberMe ->
rememberMe.key("{rememberMeKey}")
rememberMe.tokenValiditySeconds({tokenValiditySeconds})
rememberMe.rememberMeParameter("{rememberMeParameter}")
rememberMe.rememberMeCookieName("{rememberMeCookieName}")
rememberMe.useSecureCookie({useSecureCookie})
rememberMe.userDetailsService({userDetailsServiceBean})
rememberMe.tokenRepository({tokenRepositoryBean})
rememberMe.alwaysRemember({alwaysRemember})
rememberMe.rememberMeCookieDomain("{cookieDomain}") // if cookieDomain is not blank
rememberMe.authenticationSuccessHandler({authSuccessHandlerBean}) // if bean is set
rememberMe.rememberMeServices({rememberMeServicesBean}) // if bean is set (overrides default)
}Variables
Same as Java variant.
SessionManagement DSL fragment (Java)
Insert Point
Inside filterChain method body, before return http.build();
Code
// defaults: enabled, sessionFixation=CHANGE_SESSION_ID, sessionCreationPolicy=IF_REQUIRED
// When all options are default — skip this fragment entirely
// When disabled=true:
http.sessionManagement(sessionManagement -> sessionManagement.disable());
// Boot < 3.1, with options:
http.sessionManagement(sessionManagement -> sessionManagement
.sessionFixation(sessionFixation -> sessionFixation.{fixationMethod}()) // if sessionFixation is not CHANGE_SESSION_ID; options: none, newSession, migrateSession
.sessionCreationPolicy(org.springframework.security.config.http.SessionCreationPolicy.{POLICY}) // if sessionCreationPolicy is not IF_REQUIRED; options: ALWAYS, NEVER, STATELESS
.invalidSessionUrl("{invalidSessionUrl}") // if invalidSessionUrl is not blank
.enableSessionUrlRewriting(true) // if enableSessionUrlRewriting is true
.sessionAuthenticationErrorUrl("{authErrorUrl}") // if authErrorUrl is not blank
.maximumSessions({maximumSessions}) // ALL below require maximumSessions != null
.maxSessionsPreventsLogin(true) // if maximumSessionsPreventsLogin is true
.expiredUrl("{expiredUrl}") // if expiredUrl is not blank
.expiredSessionStrategy({expiredSessionStrategyBean}) // if expiredSessionStrategy is set
.sessionRegistry({sessionRegistryBean}) // if sessionRegistry is set
.sessionAuthenticationStrategy({sessionAuthStrategyBean}) // if sessionAuthenticationStrategy is set
.invalidSessionStrategy({invalidSessionStrategyBean}) // if invalidSessionStrategy is set
.sessionAuthenticationFailureHandler({sessionFailureHandlerBean}) // if sessionFailureHandler is set
);
// Boot >= 3.1, with options:
// IMPORTANT: sessionConcurrency sub-fields are INDEPENDENT of maximumSessions
http.sessionManagement(sessionManagement -> sessionManagement
.sessionFixation(sessionFixation -> sessionFixation.{fixationMethod}()) // if sessionFixation is not CHANGE_SESSION_ID
.sessionCreationPolicy(org.springframework.security.config.http.SessionCreationPolicy.{POLICY}) // if sessionCreationPolicy is not IF_REQUIRED
.invalidSessionUrl("{invalidSessionUrl}") // if invalidSessionUrl is not blank
.enableSessionUrlRewriting(true) // if enableSessionUrlRewriting is true
.sessionAuthenticationErrorUrl("{authErrorUrl}") // if authErrorUrl is not blank
.sessionConcurrency(sessionConcurrency -> sessionConcurrency
.maximumSessions({maximumSessions}) // if maximumSessions is set (independent null-check)
.maxSessionsPreventsLogin(true) // if maximumSessionsPreventsLogin is true (independent)
.expiredUrl("{expiredUrl}") // if expiredUrl is not blank (independent)
.expiredSessionStrategy({expiredSessionStrategyBean}) // if expiredSessionStrategy is set (independent)
.sessionRegistry({sessionRegistryBean}) // if sessionRegistry is set (independent)
)
.sessionAuthenticationStrategy({sessionAuthStrategyBean}) // if sessionAuthenticationStrategy is set
.invalidSessionStrategy({invalidSessionStrategyBean}) // if invalidSessionStrategy is set
.sessionAuthenticationFailureHandler({sessionFailureHandlerBean}) // if sessionFailureHandler is set
);Variables
| Variable | Source | Default |
|---|---|---|
{fixationMethod} | user choice | skip if default (changeSessionId); options: none, newSession, migrateSession |
{POLICY} | user choice | skip if default (IF_REQUIRED); options: ALWAYS, NEVER, STATELESS |
{invalidSessionUrl} | user input | skip if empty |
{authErrorUrl} | user input | skip if empty |
{maximumSessions} | user input (int) | skip if not set |
{expiredUrl} | user input | skip if empty |
{expiredSessionStrategyBean} | user input (bean ref) | skip if not set |
{sessionRegistryBean} | user input (bean ref) | skip if not set |
{sessionAuthStrategyBean} | user input (bean ref) | skip if not set |
{invalidSessionStrategyBean} | user input (bean ref) | skip if not set |
{sessionFailureHandlerBean} | user input (bean ref) | skip if not set |
SessionManagement DSL fragment (Kotlin)
Insert Point
Inside filterChain method body, before return http.build()
Code
// defaults: enabled, sessionFixation=CHANGE_SESSION_ID, sessionCreationPolicy=IF_REQUIRED
// When all options are default — skip this fragment entirely
// When disabled=true:
http.sessionManagement { it.disable() }
// Boot < 3.1, with options:
http.sessionManagement { sessionManagement ->
sessionManagement
.sessionFixation { sessionFixation -> sessionFixation.{fixationMethod}() }
.sessionCreationPolicy(org.springframework.security.config.http.SessionCreationPolicy.{POLICY})
.invalidSessionUrl("{invalidSessionUrl}")
.enableSessionUrlRewriting(true)
.sessionAuthenticationErrorUrl("{authErrorUrl}")
.maximumSessions({maximumSessions}) // ALL below require maximumSessions != null
.maxSessionsPreventsLogin(true)
.expiredUrl("{expiredUrl}")
.expiredSessionStrategy({expiredSessionStrategyBean})
.sessionRegistry({sessionRegistryBean})
.sessionAuthenticationStrategy({sessionAuthStrategyBean})
.invalidSessionStrategy({invalidSessionStrategyBean})
.sessionAuthenticationFailureHandler({sessionFailureHandlerBean})
}
// Boot >= 3.1, with options:
// IMPORTANT: sessionConcurrency sub-fields are INDEPENDENT of maximumSessions
http.sessionManagement { sessionManagement ->
sessionManagement
.sessionFixation { sessionFixation -> sessionFixation.{fixationMethod}() }
.sessionCreationPolicy(org.springframework.security.config.http.SessionCreationPolicy.{POLICY})
.invalidSessionUrl("{invalidSessionUrl}")
.enableSessionUrlRewriting(true)
.sessionAuthenticationErrorUrl("{authErrorUrl}")
.sessionConcurrency { sessionConcurrency ->
sessionConcurrency
.maximumSessions({maximumSessions}) // if maximumSessions is set (independent null-check)
.maxSessionsPreventsLogin(true) // if maximumSessionsPreventsLogin is true (independent)
.expiredUrl("{expiredUrl}") // if expiredUrl is not blank (independent)
.expiredSessionStrategy({expiredSessionStrategyBean}) // if expiredSessionStrategy is set (independent)
.sessionRegistry({sessionRegistryBean}) // if sessionRegistry is set (independent)
}
.sessionAuthenticationStrategy({sessionAuthStrategyBean})
.invalidSessionStrategy({invalidSessionStrategyBean})
.sessionAuthenticationFailureHandler({sessionFailureHandlerBean})
}Variables
| Variable | Source | Default |
|---|---|---|
{fixationMethod} | user choice | skip if default; options: none, newSession, migrateSession |
{POLICY} | user choice | skip if default; options: ALWAYS, NEVER, STATELESS |
{invalidSessionUrl} | user input | skip if empty |
{authErrorUrl} | user input | skip if empty |
{maximumSessions} | user input (int) | skip if not set |
{expiredUrl} | user input | skip if empty |
{expiredSessionStrategyBean} | user input (bean ref) | skip if not set |
{sessionRegistryBean} | user input (bean ref) | skip if not set |
{sessionAuthStrategyBean} | user input (bean ref) | skip if not set |
{invalidSessionStrategyBean} | user input (bean ref) | skip if not set |
{sessionFailureHandlerBean} | user input (bean ref) | skip if not set |
Authorization Server Properties
application.properties
# Server settings:
spring.security.oauth2.authorizationserver.issuer={issuer}
spring.security.oauth2.authorizationserver.endpoint.authorization-uri={authorizationUri}
spring.security.oauth2.authorizationserver.endpoint.logout-uri={logoutUri}
spring.security.oauth2.authorizationserver.endpoint.client-registration-uri={clientRegistrationUri}
spring.security.oauth2.authorizationserver.endpoint.user-info-uri={userInfoUri}
spring.security.oauth2.authorizationserver.endpoint.device-authorization-uri={deviceAuthorizationUri}
spring.security.oauth2.authorizationserver.endpoint.device-verification-uri={deviceVerificationUri}
spring.security.oauth2.authorizationserver.endpoint.token-uri={tokenUri}
spring.security.oauth2.authorizationserver.endpoint.jwk-set-uri={jwkSetUri}
spring.security.oauth2.authorizationserver.endpoint.token-revocation-uri={tokenRevocationUri}
spring.security.oauth2.authorizationserver.endpoint.token-introspection-uri={tokenIntrospectionUri}
# Per client:
spring.security.oauth2.authorizationserver.client.{clientName}.require-proof-key={bool}
spring.security.oauth2.authorizationserver.client.{clientName}.require-authorization-consent={bool}
spring.security.oauth2.authorizationserver.client.{clientName}.token-endpoint-authentication-signing-algorithm={algorithm}
spring.security.oauth2.authorizationserver.client.{clientName}.registration.client-id={clientId}
spring.security.oauth2.authorizationserver.client.{clientName}.registration.client-secret=${AUTHSERVER_{CLIENT_NAME_UPPER}_CLIENT_SECRET}
spring.security.oauth2.authorizationserver.client.{clientName}.registration.client-authentication-methods={methods}
spring.security.oauth2.authorizationserver.client.{clientName}.registration.authorization-grant-types={types}
spring.security.oauth2.authorizationserver.client.{clientName}.registration.redirect-uris={uris}
spring.security.oauth2.authorizationserver.client.{clientName}.registration.post-logout-redirect-uris={uris}
spring.security.oauth2.authorizationserver.client.{clientName}.registration.scopes={scopes}
spring.security.oauth2.authorizationserver.client.{clientName}.token.authorization-code-time-to-live={seconds}
spring.security.oauth2.authorizationserver.client.{clientName}.token.access-token-time-to-live={seconds}
spring.security.oauth2.authorizationserver.client.{clientName}.token.device-code-time-to-live={seconds}
spring.security.oauth2.authorizationserver.client.{clientName}.token.refresh-token-time-to-live={seconds}
spring.security.oauth2.authorizationserver.client.{clientName}.token.reuse-refresh-tokens={bool}
spring.security.oauth2.authorizationserver.client.{clientName}.token.id-token-signature-algorithm={algorithm}Note: Endpoint fields only written if different from defaults. Collection fields (methods, grant-types, uris, scopes) joined with ,.
Client secret handling (security)
Never substitute a literal client secret into the file. Always emit ${AUTHSERVER_{CLIENT_NAME_UPPER}_CLIENT_SECRET} where {CLIENT_NAME_UPPER} is the uppercased {clientName} (non-alphanum replaced with _). The encoded-secret prefix ({bcrypt} / {noop}) belongs to the env-var value at runtime, not to the property file. After generation, report to the user the exact env var name that must be set before running the app. Do NOT ask the user for the secret value — it must not enter the conversation.
Variables
| Variable | Source | Default |
|---|---|---|
{issuer} | user input | skip if empty |
{clientName} | user input | — |
{CLIENT_NAME_UPPER} | {clientName} uppercased, non-alphanum replaced with _ | — |
{clientId} | user input | — |
{authorizationUri} | user input | skip if default |
{logoutUri} | user input | skip if default |
{clientRegistrationUri} | user input | skip if default |
{userInfoUri} | user input | skip if default |
{deviceAuthorizationUri} | user input | skip if default |
{deviceVerificationUri} | user input | skip if default |
{tokenUri} | user input | skip if default |
{jwkSetUri} | user input | skip if default |
{tokenRevocationUri} | user input | skip if default |
{tokenIntrospectionUri} | user input | skip if default |
{bool} | user input | false |
{algorithm} | user input | skip if default (RS256) |
{methods} | user input (comma-separated) | — |
{types} | user input (comma-separated) | — |
{uris} | user input (comma-separated) | — |
{scopes} | user input (comma-separated) | — |
{seconds} | user input | skip if default (300/300/300/3600) |
JWT Properties
application.properties
# When jwkSetUriType=SPRING:
spring.security.oauth2.resourceserver.jwt.issuer-uri={issuerUri}
spring.security.oauth2.resourceserver.jwt.jwk-set-uri={jwkSetUri}
# When jwkSetUriType=CUSTOM:
{jwkSetCustomUriPropertyKey}={jwkSetCustomUri}
# When jwsAlgorithm != RS256:
spring.security.oauth2.resourceserver.jwt.jws-algorithms={jwsAlgorithm}Variables
| Variable | Source | Default |
|---|---|---|
{issuerUri} | user input | skip if empty |
{jwkSetUri} | user input | skip if empty |
{jwkSetCustomUriPropertyKey} | user input | only for CUSTOM type |
{jwkSetCustomUri} | user input | only for CUSTOM type |
{jwsAlgorithm} | user choice | RS256 (default, skip if RS256) |
LDAP application properties
When to write
LDAP_STATEFUL authentication type. Properties are extracted via @Value field injection into the configuration class.
Properties
| Key | Value | Condition |
|---|---|---|
{appPrefix}.ldap.url | ldap(s)://{host}:{port} | always |
{appPrefix}.ldap.managerDn | {managerDn} | if anonymousAccess=false |
{appPrefix}.ldap.managerPassword | ${LDAP_MANAGER_PASSWORD} | if anonymousAccess=false |
{appPrefix}.ldap.userDnPatterns | {userDnPatterns} (comma-separated) | if userDnPatterns not empty |
{appPrefix}.ldap.groupSearchBase | {groupSearchBase} | if authorities=GROUP or GROUP_WITH_HIERARCHY |
Manager password handling (security)
Never substitute a literal manager password into the file. Always emit the value as ${LDAP_MANAGER_PASSWORD}. After generation, report to the user the exact env var name that must be set before running the app (shell export, IDE run config, or deployment secret store). Do NOT ask the user for the password value — it must not enter the conversation.
Each property is injected as a @Value-annotated field. Replace {appPrefix} with the actual value from Step 1.
Example — if appPrefix = spring-petclinic:
@Value("${spring-petclinic.ldap.url}") private String ldapUrl;
@Value("${spring-petclinic.ldap.managerDn}") private String ldapManagerDn;Example — if appPrefix = app (default):
@Value("${app.ldap.url}") private String ldapUrl;
@Value("${app.ldap.managerDn}") private String ldapManagerDn;Notes
{appPrefix}is the application prefix (default:app), determined from existing@Valueannotations or project conventions- In the generated
@Valueannotation the property placeholder is${<actual-prefix>.ldap.<key>}— always substitute the real prefix, never leave{appPrefix}literally in the code - URL is constructed from host, port, and SSL flag:
ldaps://{host}:{port}(if SSL) orldap://{host}:{port}
OAuth2 Client Properties
application.properties
spring.security.oauth2.client.registration.{prefix}.client-id={clientId}
spring.security.oauth2.client.registration.{prefix}.provider={prefix}
spring.security.oauth2.client.registration.{prefix}.client-secret=${OAUTH_{PREFIX_UPPER}_CLIENT_SECRET}
spring.security.oauth2.client.registration.{prefix}.client-name={name}
spring.security.oauth2.client.registration.{prefix}.client-authentication-method={clientAuthMethod}
spring.security.oauth2.client.registration.{prefix}.authorization-grant-type={grantType}
spring.security.oauth2.client.registration.{prefix}.redirect-uri={redirectUri}
spring.security.oauth2.client.registration.{prefix}.scope={scope}
spring.security.oauth2.client.provider.{prefix}.authorization-uri={authorizationUri}
spring.security.oauth2.client.provider.{prefix}.token-uri={tokenUri}
spring.security.oauth2.client.provider.{prefix}.user-info-uri={userInfoUri}
spring.security.oauth2.client.provider.{prefix}.user-info-authentication-method={userInfoAuthMethod}
spring.security.oauth2.client.provider.{prefix}.user-name-attribute={userNameAttribute}
spring.security.oauth2.client.provider.{prefix}.jwk-set-uri={jwkSetUri}
spring.security.oauth2.client.provider.{prefix}.issuer-uri={issuerUri}Note: client-id, provider, client-secret, client-name are always written. All other fields are only written if non-empty.
For predefined providers (GOOGLE, GITHUB, FACEBOOK, OKTA), the provider.* section is NOT needed -- Spring auto-discovers those.
Client secret handling (security)
Never substitute a literal client-secret value into the file. Always emit ${OAUTH_{PREFIX_UPPER}_CLIENT_SECRET} where {PREFIX_UPPER} is the uppercased {prefix} (e.g. ${OAUTH_KEYCLOAK_CLIENT_SECRET}). After generation, report to the user the exact env var name that must be set before running the app (e.g. via shell export, IDE run config, or a deployment secret store). Do NOT ask the user for the secret value — it must not enter the conversation.
Variables
| Variable | Source | Default |
|---|---|---|
{prefix} | derived from provider name (lowercase, non-alphanum replaced with _) | — |
{PREFIX_UPPER} | {prefix} uppercased, non-alphanum replaced with _ | — |
{clientId} | user input | — |
{name} | user input | provider name |
{clientAuthMethod} | user input | skip if empty |
{grantType} | user input | skip if empty |
{redirectUri} | user input | skip if empty |
{scope} | user input | skip if empty |
{authorizationUri} | user input | skip if empty |
{tokenUri} | user input | skip if empty |
{userInfoUri} | user input | skip if empty |
{userInfoAuthMethod} | user input | skip if empty |
{userNameAttribute} | user input | skip if empty |
{jwkSetUri} | user input | skip if empty |
{issuerUri} | user input | skip if empty |
Java Security Configuration class + filterChain method
Code
package {packageName};
@org.springframework.context.annotation.Configuration
@org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
public class {className} {
@org.springframework.context.annotation.Bean
public org.springframework.security.web.SecurityFilterChain filterChain(org.springframework.security.config.annotation.web.builders.HttpSecurity http) throws Exception {
// DSL calls inserted here
return http.build();
}
}Variables
| Variable | Source | Default |
|---|---|---|
{packageName} | project context | — |
{className} | user choice | SecurityConfiguration |
Kotlin Security Configuration class + filterChain method
Code
package {packageName}
@org.springframework.context.annotation.Configuration
@org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
class {className} {
@org.springframework.context.annotation.Bean
fun filterChain(http: org.springframework.security.config.annotation.web.builders.HttpSecurity): org.springframework.security.web.SecurityFilterChain {
// DSL calls inserted here
return http.build()
}
}Variables
| Variable | Source | Default |
|---|---|---|
{packageName} | project context | — |
{className} | user choice | SecurityConfiguration |
Authorization Server Variant
Authentication type: OAUTH_AUTHORIZATION_SERVER Requires: Spring Boot >= 3.1
Fragments Used
1. Common DSL (see references/common-dsl.md) — all blocks (headers, anonymous, csrf always generated)
Dependencies
_dependencies/base.md(always)_dependencies/authorization-server.md
Properties
_properties/authorization-server/properties.md
Questions
Issuer Question
Authorization server Issuer URI? [empty]Endpoint Questions (batch, skip if "all defaults")
Endpoint settings (leave empty for defaults):
1. **Authorization URI?** [/oauth2/authorize]
2. **JWK Set URI?** [/oauth2/jwks]
3. **Token URI?** [/oauth2/token]
4. **Token Revocation URI?** [/oauth2/revoke]
5. **Token Introspection URI?** [/oauth2/introspect]
6. **Device Authorization URI?** [/oauth2/device_authorization]
7. **Device Verification URI?** [/oauth2/device_verification]
8. **Logout URI?** [/connect/logout]
9. **Client Registration URI?** [/connect/register]
10. **User Info URI?** [/userinfo]Client Questions (repeat per client)
Add client? [yes/no]If yes:
Client settings:
1. **Client name?** []
2. **Client ID?** []
3. **Authentication methods?** [client_secret_basic] (comma-separated)
4. **Grant types?** [authorization_code] (comma-separated)
5. **Redirect URIs?** [] (comma-separated)
6. **Scopes?** [openid] (comma-separated)Do NOT ask the user for the client secret. The secret value must not enter the conversation. The skill emits ${AUTHSERVER_{CLIENT_NAME_UPPER}_CLIENT_SECRET} in application.properties as a placeholder — see _properties/authorization-server/properties.md → "Client secret handling". After generation, tell the user the exact env var name and instruct them to set it (the encoded value with {bcrypt}/{noop} prefix) via shell export, IDE run config, or deployment secret store.
Client Advanced Questions (batch, skip if "all defaults")
Advanced client settings {clientName}:
1. **Post-logout redirect URIs?** [empty] (comma-separated)
2. **Require Proof Key (PKCE)?** [no]
3. **Require Authorization Consent?** [no]Token Configuration Questions (batch, skip if "all defaults")
Token settings for client {clientName}:
1. **Authorization code TTL (seconds)?** [300]
2. **Access token TTL (seconds)?** [300]
3. **Device code TTL (seconds)?** [300]
4. **Refresh token TTL (seconds)?** [3600]
5. **Reuse refresh tokens?** [yes]
6. **Token signing algorithm?** [RS256] (maps to token-endpoint-authentication-signing-algorithm, NOT id-token-signature-algorithm)Generation
Auth Server DSL order: authorizeHttpRequests → headers → anonymous → csrf (same as Custom)
1. Read skeleton from _skeletons/{lang}.md 2. Insert http.authorizeHttpRequests(...) fragment (FIRST in filterChain) 3. Insert http.headers(Customizer.withDefaults()) 4. Insert http.anonymous(Customizer.withDefaults()) 5. Insert http.csrf(Customizer.withDefaults()) 6. Add dependencies from _dependencies/base.md + _dependencies/authorization-server.md 7. Write server endpoint properties from _properties/authorization-server/properties.md (only non-default values) 8. Write client properties for each client (including advanced options: post-logout redirect URIs, require proof key, require authorization consent, device code TTL)
Notes
- NO `@Import` annotation — Spring Boot autoconfigures the Authorization Server via the
spring-boot-starter-security-oauth2-authorization-serverdependency. Do NOT add@Import(OAuth2AuthorizationServerConfiguration.class). - The Java configuration is identical to the Custom variant — just common DSL fragments. All Authorization Server-specific settings go through
application.properties. - Endpoint properties are only written when different from defaults.
- Client secret is emitted only as an env var placeholder
${AUTHSERVER_{CLIENT_NAME_UPPER}_CLIENT_SECRET}. The encoded-secret prefix ({bcrypt}/{noop}) belongs to the env var value, not to the property file. - Token TTL properties are only written when different from defaults (auth code: 300s, access: 300s, device: 300s, refresh: 3600s).
Common DSL Fragments
These fragments are included in ALL authentication types.
Generation Order (inside filterChain) — VARIANT-DEPENDENT
HTTP Session (full DSL): 1. Authorize requests (_fragments/authorize-requests/{lang}.md) -- FIRST 2. Headers (_fragments/headers/{lang}.md) -- if enabled (Customizer.withDefaults() by default) 3. Session Management (_fragments/session-management/{lang}.md) -- only if non-default session options are set (skip entirely when all defaults) 4. Exception Handling (_fragments/exception-handling/{lang}.md) -- if enabled 5. Remember Me (_fragments/remember-me/{lang}.md) -- if enabled (BEFORE formLogin!) 6. Form Login (_fragments/form-login/{lang}.md) -- variant-specific 7. Logout (_fragments/logout/{lang}.md) -- if non-default logout options 8. Anonymous (_fragments/anonymous/{lang}.md) -- if enabled 9. CSRF (_fragments/csrf/{lang}.md) -- if enabled 10. User Details Service -- if user storage is configured
Custom / Authorization Server / LDAP (bare minimum): 1. Authorize requests -- FIRST 2. Headers (Customizer.withDefaults()) 3. Anonymous (Customizer.withDefaults()) 4. CSRF (Customizer.withDefaults()) 5. NO sessionManagement, formLogin, or logout — even for LDAP despite its "Http Session" label!
JWT (stateless): 1. Authorize requests -- FIRST 2. Headers 3. oauth2ResourceServer (variant-specific) 4. Anonymous 5. CSRF (NOT auto-disabled! kept as Customizer.withDefaults()) 6. No sessionManagement generated
OAuth2/OIDC (stateful): 1. oauth2Login (variant-specific) -- FIRST (with userInfoEndpoint mapper) 2. Logout with OIDC handler -- SECOND 3. Authorize requests -- THIRD 4. Headers 5. Anonymous 6. CSRF
Headers Questions (batch, skip if "all defaults")
Security headers settings:
1. **Disable headers entirely?** [no]
2. **Frame Options?** [DENY] (Disabled / DENY / SAME_ORIGIN)
3. **Referrer Policy?** [NO_REFERRER] (NO_REFERRER / NO_REFERRER_WHEN_DOWNGRADE / SAME_ORIGIN / ORIGIN / STRICT_ORIGIN / ORIGIN_WHEN_CROSS_ORIGIN / STRICT_ORIGIN_WHEN_CROSS_ORIGIN / UNSAFE_URL)
4. **Permissions Policy?** [empty]Advanced Headers Questions (batch, skip if "all defaults")
Advanced headers settings:
1. **Disable defaults and select manually?** [no]
2. **Content Type Options?** [yes] (only if defaults are disabled)
3. **XSS Protection?** [yes] (only if defaults are disabled)
4. **Cache Control?** [yes] (only if defaults are disabled)
5. **HSTS?** [yes] (only if defaults are disabled)
6. **Header Writers?** [empty] (list of HeaderWriter bean references, comma-separated)If all answers are defaults, skip the headers fragment entirely. If disabled=true, use the headers.disable() variant. If disableDefaults=true, use the headers.defaultsDisabled() variant and individually enable selected sub-options.
CSRF Question
Disable CSRF? [no]If yes: use csrf.disable() variant. If user provides ignore patterns: use the ignoringRequestMatchers variant.
Advanced CSRF Questions (skip if "all defaults")
Advanced CSRF settings:
1. **Ignore patterns (Ant matches)?** [empty] (comma-separated)
2. **CSRF Token Repository bean?** [empty]
3. **Require CSRF Protection Matcher bean?** [empty]
4. **Session Authentication Strategy bean?** [empty]Anonymous Question
Disable anonymous access? [no]If yes: use anonymous.disable() variant.
Advanced Anonymous Questions (skip if "all defaults")
Advanced anonymous access settings:
1. **Key?** [empty]
2. **Authorities?** [empty] (comma-separated)
3. **Authentication provider bean?** [empty]
4. **Authentication filter bean?** [empty]Authorize Requests Questions
Request authorization rules:Ask user for URL patterns and their permissions. Default: anyRequest().authenticated().
Use list_spring_security_roles to suggest available roles.
If user specifies a security matcher, add http.securityMatcher(...) before the authorize block.
Matcher Options
For each rule, the matcher type can be:
- All paths (default) — uses
anyRequest() - Request matcher — uses
.requestMatchers("{pattern}")with optional HTTP method - Dispatcher type — uses
.dispatcherTypeMatchers(DispatcherType.{TYPE})(FORWARD, INCLUDE, REQUEST, ASYNC, ERROR)
Permission Options
permitAll()— allow allauthenticated()— require authenticationhasRole("{role}")— single rolehasAnyRole("{role1}", "{role2}")— multiple roleshasAuthority("{authority}")— single authorityhasAnyAuthority("{a1}", "{a2}")— multiple authoritiesdenyAll()— deny allanonymous()— anonymous onlyrememberMe()— remember-me onlyfullyAuthenticated()— fully authenticated only
Custom Variant
Authentication type: CUSTOM
Description
A minimal security configuration with only the common DSL blocks (headers, CSRF, anonymous, authorize requests). No specific authentication mechanism is configured — the user is expected to add their own authentication logic manually.
Fragments Used
1. Common DSL (see references/common-dsl.md) — all blocks
Dependencies
_dependencies/base.md(always)
Properties
None.
Questions
No variant-specific questions. Only common DSL questions apply (headers, CSRF, anonymous, authorize requests).
Generation
Custom DSL order: authorizeHttpRequests → headers → anonymous → csrf
1. Read skeleton from _skeletons/{lang}.md 2. Insert http.authorizeHttpRequests(...) fragment (FIRST in filterChain) 3. Insert http.headers(Customizer.withDefaults()) 4. Insert http.anonymous(Customizer.withDefaults()) 5. Insert http.csrf(Customizer.withDefaults()) 6. Add dependencies from _dependencies/base.md 7. No properties file to write
Notes
- This is the simplest configuration — a bare
SecurityFilterChainwith only authorization rules and common protections. - Useful when the user wants to set up authentication manually or integrate a custom authentication mechanism.
- The generated class will have
@Configuration @EnableWebSecurityand a singlefilterChainmethod.
HTTP Session (Form Login) Variant
Authentication type: HTTP_SESSION_STATEFUL
Fragments Used
1. Common DSL (see references/common-dsl.md) 2. FormLogin (_fragments/form-login/{lang}.md) 3. Logout (_fragments/logout/{lang}.md) — only if non-default logout options 4. SessionManagement (_fragments/session-management/{lang}.md) 5. Exception Handling (_fragments/exception-handling/{lang}.md) — only if Access Denied Handling is enabled 6. Remember Me (_fragments/remember-me/{lang}.md) — only if enabled 7. User Storage bean (_beans/user-storage/{lang}.md) — always (default: In memory)
Dependencies
_dependencies/base.md(always)
Questions
FormLogin Questions (batch)
Form login settings:
1. **Login page URL?** [default Spring Security]
2. **Failure URL?** [default]
3. **Success URL?** [default]If user provides no URLs, generate http.formLogin(Customizer.withDefaults()).
FormLogin Advanced Questions (batch, skip if "all defaults")
Advanced form login settings:
1. **Username parameter?** [default]
2. **Password parameter?** [default]
3. **Login processing URL?** [default]
4. **Failure forward URL?** [default]
5. **Success forward URL?** [default]
6. **Authentication details source bean?** [empty]Handler Type Question (only if URLs are set)
Success/failure handler type?
1. HTTP status (default) -- inline lambda
2. Bean -- reference to existing beanIf STATUS: ask for HTTP status codes (e.g., OK, UNAUTHORIZED). If BEAN: ask for bean references.
Logout Questions (batch, skip if "all defaults")
Logout settings:
1. **Disable logout?** [no]
2. **Logout URL?** [/logout]
3. **Post-logout URL?** [/login?logout]
4. **Clear authentication?** [yes]
5. **Invalidate session?** [yes]
6. **Delete cookies?** [empty] (comma-separated)Logout Advanced Questions (batch, skip if "all defaults")
Advanced logout settings:
1. **Logout request matcher bean?** [empty]
2. **Logout handler bean(s)?** [empty]
3. **Logout success handler type?** [URL] (URL / Status / Bean)
4. **Logout success status code?** [empty] (only for Status type)
5. **Logout success handler bean?** [empty] (only for Bean type)If all defaults — skip the logout fragment entirely (Spring Security enables default logout automatically). If disabled=true — use logout.disable() variant. If any non-default option — use the "with options" variant.
Access Denied Handling Question
Configure Access Denied handling? [no]If yes:
Access Denied settings:
1. **Disable Exception Handling?** [no]
2. **Access Denied Page URL?** [empty] (i.e. /errors/401)
3. **AccessDeniedHandler bean?** [empty]
4. **Handler type?** [Status] (Status / Bean) — Status creates HttpStatusAccessDeniedHandler, Bean — bean reference
5. **Authentication entry point?** [empty] (HTTP status code, creates HttpStatusEntryPoint)Remember Me Question
Enable Remember Me? [no]If yes:
Remember Me settings:
1. **Key?** [empty]
2. **Token validity (seconds)?** [1209600 = 2 weeks]
3. **Use persistent tokens?** [no]Remember Me Advanced Questions (batch, skip if "all defaults")
Advanced Remember Me settings:
1. **Cookie domain?** [empty]
2. **Remember Me parameter?** [remember-me]
3. **Cookie name?** [remember-me]
4. **Secure cookie?** [default]
5. **Always remember?** [no]
6. **Authentication success handler bean?** [empty]
7. **Remember Me services bean?** [empty] (overrides default implementation)Session Management Questions (batch, skip if "all defaults")
Session settings:
1. **Disable Session Management?** [no]
2. **Session Fixation?** [CHANGE_SESSION_ID] (NONE / CHANGE_SESSION_ID / NEW_SESSION / MIGRATE_SESSION)
3. **Invalid session URL?** [empty]
4. **Session creation policy?** [IF_REQUIRED] (IF_REQUIRED / ALWAYS / NEVER / STATELESS)
5. **Authentication error URL?** [empty]
6. **URL rewriting?** [no]
7. **Maximum sessions?** [empty]
8. **Block login when max exceeded?** [no] (only if max is set)
9. **Expired session URL?** [empty] (only if max is set)
10. **Session registry bean?** [empty] (only if max is set)If all defaults, skip session management fragment entirely.
Session Management Advanced Questions (batch, skip if "all defaults")
Advanced session settings:
1. **Expired session strategy bean?** [empty]
2. **Session authentication strategy bean?** [empty]
3. **Invalid session strategy bean?** [empty]
4. **Session authentication failure handler bean?** [empty]User Storage Question
User storage type?
1. In memory (default) -- InMemoryUserDetailsManager
2. JDBC -- JdbcUserDetailsManager
3. JPA -- UserDetailsService bean reference
4. Custom -- UserDetailsService bean referenceIf IN_MEMORY:
Users (can add multiple):
| Username | Password | Roles |
|----------|----------|-------|
| admin | admin | ADMIN |
| user | user | USER |If JDBC: add DataSource constructor parameter to the class. If JPA/CUSTOM: ask for bean reference.
Generation
HTTP Session DSL order: authorizeHttpRequests → headers → sessionManagement → exceptionHandling → rememberMe → formLogin → logout → anonymous → csrf → userDetailsService
1. Read skeleton from _skeletons/{lang}.md 2. Insert http.authorizeHttpRequests(...) fragment (FIRST in filterChain) 3. Insert http.headers(...) — Customizer.withDefaults() by default 4. Insert http.sessionManagement(...) fragment — only if non-default session options are set (skip entirely when all defaults) 5. Insert http.exceptionHandling(...) — only if Access Denied is enabled 6. Insert http.rememberMe(...) — only if enabled (BEFORE formLogin!) 7. Insert http.formLogin(...) fragment — Customizer.withDefaults() by default 8. Insert http.logout(...) fragment — only if non-default logout options 9. Insert http.anonymous(...) — Customizer.withDefaults() by default 10. Insert http.csrf(...) — Customizer.withDefaults() by default 11. Add User Storage method to class body from _beans/user-storage/{lang}.md 12. Add http.userDetailsService(inMemoryUserDetailsService()) (or JDBC/JPA/Custom equivalent) to filterChain 13. Add dependencies from _dependencies/base.md
Notes
- Logout default behavior: Spring Security enables logout at
/logoutby default. Thehttp.logout(...)DSL is only needed when customizing logout behavior. Do NOT addhttp.logout(Customizer.withDefaults())— it's redundant. - User Storage is NOT a @Bean: The
inMemoryUserDetailsService()method is a plain method (not annotated with@Bean). It is called inline from the filterChain viahttp.userDetailsService(inMemoryUserDetailsService()). - No orphan methods: Every generated method must be called from filterChain or from another bean.
JWT (OAuth2 Resource Server) Variant
Authentication type: JWT_STATELESS
Fragments Used
1. Common DSL (see references/common-dsl.md) 2. JWT (_fragments/jwt/{lang}.md) 3. JWT Converter bean (_beans/jwt-converter/{lang}.md) -- optional
Dependencies
_dependencies/base.md(always)_dependencies/resource-server.md(always for JWT)
Properties
_properties/jwt/properties.md
Questions
Provider Question
OAuth2 provider for JWT?
1. No provider (manual setup) (default)
2. Keycloak
3. AWS Cognito
4. Okta
5. OtherJWK Set URI Question
JWK Set URI type?
1. Standard Spring property (default) -- spring.security.oauth2.resourceserver.jwt.jwk-set-uri
2. Custom property key -- injection via @ValueIf SPRING: ask for issuer URI and JWK set URI values. If CUSTOM: ask for property key name and URI value.
JWT Converter Question (only for Keycloak/AWS Cognito)
Generate JwtAuthenticationConverter for role mapping? [no]If yes: add _beans/jwt-converter/{lang}.md to the class body. The converter uses:
"roles"claim for Keycloak"cognito:groups"claim for AWS Cognito
JWS Algorithm Question
JWT signing algorithm? [RS256]If not RS256, add jws-algorithms property.
Generation
JWT DSL order: authorizeHttpRequests FIRST, then headers, then oauth2ResourceServer, then anonymous, csrf. No sessionManagement.
1. Read skeleton from _skeletons/{lang}.md 2. Insert http.authorizeHttpRequests(...) fragment (FIRST in filterChain) 3. Insert http.headers(...) — Customizer.withDefaults() by default 4. Insert JWT fragment from _fragments/jwt/{lang}.md — http.oauth2ResourceServer(...) 5. Insert http.anonymous(...) — Customizer.withDefaults() by default 6. Insert http.csrf(...) — Customizer.withDefaults() by default. CSRF is NOT auto-disabled for JWT; it is controlled by the user via the CSRF question in common DSL 7. If generateConverter=true: add converter helper from _beans/jwt-converter/{lang}.md, reference it in the JWT DSL via .jwtAuthenticationConverter(jwtAuthenticationConverter()) 8. CRITICAL: The converter method is NOT @Bean — it is a package-private helper method called directly from filterChain 9. If jwkSetUriType=CUSTOM: add @Value field to class for JWK Set URI 10. Add dependencies from _dependencies/base.md + _dependencies/resource-server.md 11. Write JWT properties from _properties/jwt/properties.md
LDAP Variant
Authentication type: LDAP_STATEFUL
Fragments Used
1. Common DSL (see references/common-dsl.md) — all blocks (headers, anonymous, csrf always generated with Customizer.withDefaults()) 2. LDAP Context Source bean (_beans/ldap-context-source/{lang}.md) — real @Bean, not helper method 3. LDAP Authorities Populator bean — real @Bean (NullLdapAuthoritiesPopulator for "No authorities") 4. LDAP Auth Manager bean (_beans/ldap-auth-manager/{lang}.md) — real @Bean
Dependencies
_dependencies/base.md(always)_dependencies/ldap.md
Properties
_properties/ldap/properties.md
Questions
Connection Settings (batch)
LDAP connection settings:
1. **Host?** []
2. **Port?** [389]
3. **Use SSL (ldaps)?** [yes]
4. **Base DN?** []Access Type Question
LDAP access type?
1. Anonymous (default)
2. With manager credentialsIf authenticated:
Manager credentials:
1. **Manager DN?** []Do NOT ask the user for the manager password. The password value must not enter the conversation. The skill emits ${LDAP_MANAGER_PASSWORD} in application.properties as a placeholder — see _properties/ldap/properties.md → "Manager password handling". After generation, tell the user the exact env var name and instruct them to set it locally (shell export, IDE run config) or via their deployment's secret store.
Authentication Type Question
LDAP authentication type?
1. BIND (default) -- verify by binding
2. PASSWORD -- password comparisonIf PASSWORD: ask for password encoder bean reference.
User DN Patterns Question
User DN Patterns? (e.g., uid={0},ou=people) []Authorities Question
Authorities source?
1. No authorities (default) -- NullLdapAuthoritiesPopulator
2. By group membership -- DefaultLdapAuthoritiesPopulator
3. By group membership with hierarchy -- NestedLdapAuthoritiesPopulator
4. Custom -- custom beanIf GROUP or GROUP_WITH_HIERARCHY: ask for group search base.
Generation
LDAP DSL order: authorizeHttpRequests → headers → anonymous → csrf (same as Custom — NO sessionManagement, formLogin, or logout!)
1. Read skeleton from _skeletons/{lang}.md 2. Add @Value field injection for LDAP properties (use appPrefix from Step 1):
@Value("${appPrefix}.ldap.url") private String ldapUrl;@Value("${appPrefix}.ldap.userDnPatterns") private String ldapUserDnPatterns;- Optionally: managerDn, managerPassword, groupSearchBase
3. Insert http.authorizeHttpRequests(...) fragment (FIRST in filterChain) 4. Insert http.headers(Customizer.withDefaults()) 5. Insert http.anonymous(Customizer.withDefaults()) 6. Insert http.csrf(Customizer.withDefaults()) 7. Add @Bean contextSource() — returns DefaultSpringSecurityContextSource(ldapUrl) 8. Add @Bean ldapAuthoritiesPopulator() — returns appropriate populator:
- No authorities →
NullLdapAuthoritiesPopulator - Group member →
DefaultLdapAuthoritiesPopulator - Group member with hierarchy → use role hierarchy
- Custom → user-provided bean
9. Add @Bean ldapAuthenticationManager() — uses LdapBindAuthenticationManagerFactory (for Bind auth) or LdapPasswordComparisonAuthenticationManagerFactory (for Password auth) 10. Add dependencies from _dependencies/base.md + _dependencies/ldap.md 11. Write LDAP properties to application.properties (see _properties/ldap/properties.md)
Notes
- Property key prefix uses project artifact name: e.g.,
spring-petclinic.ldap.url(notspring.ldap.*) - Field injection with `@Value` — unlike OIDC which uses constructor injection, LDAP uses field injection
- All LDAP beans are real `@Bean` methods — unlike JWT/OIDC helper methods, these are proper Spring beans
- Despite having "Http Session Authentication (stateful, LDAP)" as auth type, the generated config does NOT include sessionManagement, formLogin, or logout
- The LDAP URL format is
ldaps://host:port/baseDn(when Secure is checked) orldap://host:port/baseDn
Advanced Options
- Authorities mapper: optional bean reference for custom
GrantedAuthoritiesMapper - User details context mapper: optional bean reference for custom
UserDetailsContextMapper
Related skills
How it compares
Use as a focused codegen snippet for JWT authority mapping—not a full Spring Security tutorial or infrastructure provisioning skill.
FAQ
Who is spring-security-configuration for?
Developers and backend developers maintaining Spring APIs who already use OAuth2 resource-server JWT and need accurate authority extraction from token claims.
When should I use spring-security-configuration?
Use it during Build backend work when you add or fix JwtAuthenticationConverter wiring in a SecurityFilterChain for Keycloak roles or Cognito groups claims.
Is spring-security-configuration safe to install?
Check the Security Audits panel on this Prism page and review generated code before merge; the skill only suggests configuration patterns and does not run deployments.