☕ #auth - Authentication Directive (Java)
#auth - Authentication Directive (Java)
The #auth
directive provides enterprise-grade authentication and authorization capabilities for Java applications, enabling secure access control with Spring Security integration and comprehensive user management.
Basic Syntax
Basic authentication
#auth {
#api /protected {
return @get_protected_data()
}
}Role-based authorization
#auth roles: ["admin", "user"] {
#api /admin-data {
return @get_admin_data()
}
}Custom authentication logic
#auth {
check: @custom_auth_check(@request.headers.Authorization)
redirect: "/login"
} {
#web /secure-page {
@render_secure_content()
}
}
Java Implementation
import org.tusklang.java.TuskLang;
import org.tusklang.java.directives.AuthDirective;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.access.prepost.PreAuthorize;@Controller
public class AuthenticatedController {
private final TuskLang tuskLang;
private final AuthDirective authDirective;
private final DataService dataService;
public AuthenticatedController(TuskLang tuskLang, DataService dataService) {
this.tuskLang = tuskLang;
this.authDirective = new AuthDirective();
this.dataService = dataService;
}
// Basic authentication with Spring Security
@GetMapping("/api/protected")
@PreAuthorize("isAuthenticated()")
public ResponseEntity<DataResponse> getProtectedData() {
return ResponseEntity.ok(dataService.getProtectedData());
}
// Role-based authorization
@GetMapping("/api/admin-data")
@PreAuthorize("hasRole('ADMIN') or hasRole('USER')")
public ResponseEntity<AdminDataResponse> getAdminData() {
return ResponseEntity.ok(dataService.getAdminData());
}
// Custom authentication logic
@GetMapping("/secure-page")
public ResponseEntity<String> getSecurePage(
@RequestHeader("Authorization") String authHeader,
HttpServletRequest request) {
if (!authDirective.checkCustomAuth(authHeader)) {
return ResponseEntity.status(401)
.header("Location", "/login")
.body("Unauthorized");
}
return ResponseEntity.ok("Secure content");
}
}
Authentication Configuration
Detailed authentication configuration
#auth {
method: "jwt" # Authentication method
roles: ["user", "admin"] # Required roles
permissions: ["read", "write"] # Required permissions
redirect: "/login" # Redirect URL for unauthenticated users
error_code: 401 # HTTP status code for auth failures
} {
#api /endpoint {
@process_request()
}
}Multiple authentication methods
#auth {
methods: ["jwt", "session", "api_key"]
fallback: "session"
priority: ["jwt", "api_key", "session"]
} {
#api /flexible-auth {
@handle_request()
}
}
Java Authentication Configuration
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.List;
import java.util.Map;@Component
@ConfigurationProperties(prefix = "tusk.auth")
public class AuthConfig {
private String defaultMethod = "jwt";
private List<String> defaultRoles = List.of("USER");
private List<String> defaultPermissions = List.of("READ");
private String redirectUrl = "/login";
private int errorCode = 401;
private Map<String, AuthMethod> methods;
private List<String> fallbackMethods;
private List<String> priorityMethods;
// Getters and setters
public String getDefaultMethod() { return defaultMethod; }
public void setDefaultMethod(String defaultMethod) { this.defaultMethod = defaultMethod; }
public List<String> getDefaultRoles() { return defaultRoles; }
public void setDefaultRoles(List<String> defaultRoles) { this.defaultRoles = defaultRoles; }
public List<String> getDefaultPermissions() { return defaultPermissions; }
public void setDefaultPermissions(List<String> defaultPermissions) { this.defaultPermissions = defaultPermissions; }
public String getRedirectUrl() { return redirectUrl; }
public void setRedirectUrl(String redirectUrl) { this.redirectUrl = redirectUrl; }
public int getErrorCode() { return errorCode; }
public void setErrorCode(int errorCode) { this.errorCode = errorCode; }
public Map<String, AuthMethod> getMethods() { return methods; }
public void setMethods(Map<String, AuthMethod> methods) { this.methods = methods; }
public List<String> getFallbackMethods() { return fallbackMethods; }
public void setFallbackMethods(List<String> fallbackMethods) { this.fallbackMethods = fallbackMethods; }
public List<String> getPriorityMethods() { return priorityMethods; }
public void setPriorityMethods(List<String> priorityMethods) { this.priorityMethods = priorityMethods; }
public static class AuthMethod {
private boolean enabled;
private String secret;
private int expiration;
private String issuer;
// Getters and setters
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
public String getSecret() { return secret; }
public void setSecret(String secret) { this.secret = secret; }
public int getExpiration() { return expiration; }
public void setExpiration(int expiration) { this.expiration = expiration; }
public String getIssuer() { return issuer; }
public void setIssuer(String issuer) { this.issuer = issuer; }
}
}
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http, AuthConfig authConfig) throws Exception {
http
.authorizeHttpRequests(authz -> authz
.requestMatchers("/public/**").permitAll()
.requestMatchers("/api/**").authenticated()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.failureUrl("/login?error=true")
)
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.invalidateHttpSession(true)
.deleteCookies("JSESSIONID")
)
.exceptionHandling(ex -> ex
.authenticationEntryPoint((request, response, authException) -> {
response.setStatus(authConfig.getErrorCode());
response.sendRedirect(authConfig.getRedirectUrl());
})
)
.csrf(csrf -> csrf.disable());
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public JwtTokenProvider jwtTokenProvider(AuthConfig authConfig) {
AuthConfig.AuthMethod jwtMethod = authConfig.getMethods().get("jwt");
return new JwtTokenProvider(jwtMethod.getSecret(), jwtMethod.getExpiration());
}
}
JWT Authentication
JWT authentication
#auth method: "jwt" {
#api /jwt-protected {
return @get_jwt_data()
}
}JWT with custom claims
#auth {
method: "jwt"
claims: {
role: @auth.user.role
permissions: @auth.user.permissions
tenant: @auth.user.tenant
}
} {
#api /multi-tenant {
return @get_tenant_data(@auth.user.tenant)
}
}JWT refresh token
#auth {
method: "jwt"
refresh: true
refresh_expiry: 604800 # 7 days
} {
#api /refresh-token {
return @refresh_jwt_token(@request.body.refresh_token)
}
}
Java JWT Implementation
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Service;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Service
public class JwtTokenProvider {
private final Key secretKey;
private final int expirationTime;
private final int refreshExpirationTime;
public JwtTokenProvider(String secret, int expirationTime) {
this.secretKey = Keys.hmacShaKeyFor(secret.getBytes());
this.expirationTime = expirationTime;
this.refreshExpirationTime = expirationTime * 7; // 7 days
}
public String generateToken(User user) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + expirationTime * 1000L);
Map<String, Object> claims = new HashMap<>();
claims.put("userId", user.getId());
claims.put("email", user.getEmail());
claims.put("role", user.getRole());
claims.put("permissions", user.getPermissions());
claims.put("tenant", user.getTenant());
return Jwts.builder()
.setClaims(claims)
.setSubject(user.getEmail())
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(secretKey, SignatureAlgorithm.HS512)
.compact();
}
public String generateRefreshToken(User user) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + refreshExpirationTime * 1000L);
return Jwts.builder()
.setSubject(user.getEmail())
.setIssuedAt(now)
.setExpiration(expiryDate)
.signWith(secretKey, SignatureAlgorithm.HS512)
.compact();
}
public Claims validateToken(String token) {
try {
return Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();
} catch (JwtException | IllegalArgumentException e) {
throw new InvalidJwtTokenException("Invalid JWT token");
}
}
public String refreshToken(String refreshToken) {
try {
Claims claims = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(refreshToken)
.getBody();
String email = claims.getSubject();
User user = userService.findByEmail(email);
return generateToken(user);
} catch (JwtException e) {
throw new InvalidJwtTokenException("Invalid refresh token");
}
}
}
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenProvider tokenProvider;
private final UserDetailsService userDetailsService;
public JwtAuthenticationFilter(JwtTokenProvider tokenProvider,
UserDetailsService userDetailsService) {
this.tokenProvider = tokenProvider;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = extractToken(request);
if (token != null && tokenProvider.validateToken(token) != null) {
Claims claims = tokenProvider.validateToken(token);
String email = claims.getSubject();
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (bearerToken != null && bearerToken.startsWith("Bearer ")) {
return bearerToken.substring(7);
}
return null;
}
}
@RestController
public class JwtAuthController {
private final JwtTokenProvider tokenProvider;
private final AuthenticationManager authenticationManager;
private final UserService userService;
public JwtAuthController(JwtTokenProvider tokenProvider,
AuthenticationManager authenticationManager,
UserService userService) {
this.tokenProvider = tokenProvider;
this.authenticationManager = authenticationManager;
this.userService = userService;
}
@PostMapping("/api/auth/login")
public ResponseEntity<AuthResponse> login(@RequestBody LoginRequest loginRequest) {
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
loginRequest.getEmail(),
loginRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
User user = userService.findByEmail(loginRequest.getEmail());
String token = tokenProvider.generateToken(user);
String refreshToken = tokenProvider.generateRefreshToken(user);
return ResponseEntity.ok(new AuthResponse(token, refreshToken));
} catch (AuthenticationException e) {
return ResponseEntity.status(401).body(null);
}
}
@PostMapping("/api/auth/refresh")
public ResponseEntity<AuthResponse> refresh(@RequestBody RefreshRequest refreshRequest) {
try {
String newToken = tokenProvider.refreshToken(refreshRequest.getRefreshToken());
return ResponseEntity.ok(new AuthResponse(newToken, refreshRequest.getRefreshToken()));
} catch (InvalidJwtTokenException e) {
return ResponseEntity.status(401).body(null);
}
}
}
Role-Based Authorization
Simple role check
#auth roles: ["admin"] {
#api /admin-only {
return @admin_operation()
}
}Multiple roles (OR logic)
#auth roles: ["admin", "moderator"] {
#api /moderated-content {
return @get_moderated_content()
}
}Role hierarchy
#auth {
roles: ["admin", "user"]
hierarchy: {
admin: ["user", "moderator"]
moderator: ["user"]
user: []
}
} {
#api /hierarchical-auth {
return @process_with_hierarchy()
}
}
Java Role-Based Authorization
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.stereotype.Service;@Service
public class RoleBasedAuthService {
private final RoleHierarchy roleHierarchy;
public RoleBasedAuthService(RoleHierarchy roleHierarchy) {
this.roleHierarchy = roleHierarchy;
}
public boolean hasRole(User user, String requiredRole) {
Collection<? extends GrantedAuthority> authorities = user.getAuthorities();
Collection<? extends GrantedAuthority> reachableAuthorities =
roleHierarchy.getReachableGrantedAuthorities(authorities);
return reachableAuthorities.stream()
.anyMatch(authority -> authority.getAuthority().equals("ROLE_" + requiredRole.toUpperCase()));
}
public boolean hasAnyRole(User user, List<String> requiredRoles) {
return requiredRoles.stream().anyMatch(role -> hasRole(user, role));
}
public boolean hasAllRoles(User user, List<String> requiredRoles) {
return requiredRoles.stream().allMatch(role -> hasRole(user, role));
}
}
@RestController
public class RoleBasedController {
private final RoleBasedAuthService authService;
private final AdminService adminService;
private final ModeratorService moderatorService;
public RoleBasedController(RoleBasedAuthService authService,
AdminService adminService,
ModeratorService moderatorService) {
this.authService = authService;
this.adminService = adminService;
this.moderatorService = moderatorService;
}
@GetMapping("/api/admin-only")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<AdminResponse> adminOnly(@AuthenticationPrincipal User user) {
return ResponseEntity.ok(adminService.performAdminOperation());
}
@GetMapping("/api/moderated-content")
@PreAuthorize("hasAnyRole('ADMIN', 'MODERATOR')")
public ResponseEntity<ModeratedContentResponse> moderatedContent(@AuthenticationPrincipal User user) {
return ResponseEntity.ok(moderatorService.getModeratedContent());
}
@PostMapping("/api/hierarchical-auth")
@PreAuthorize("hasRole('ADMIN') or hasRole('USER')")
public ResponseEntity<String> hierarchicalAuth(@AuthenticationPrincipal User user) {
if (authService.hasRole(user, "ADMIN")) {
return ResponseEntity.ok("Admin operation");
} else if (authService.hasRole(user, "USER")) {
return ResponseEntity.ok("User operation");
}
return ResponseEntity.status(403).body("Access denied");
}
}
@Configuration
public class RoleHierarchyConfiguration {
@Bean
public RoleHierarchy roleHierarchy() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy(
"ROLE_ADMIN > ROLE_MODERATOR\n" +
"ROLE_MODERATOR > ROLE_USER\n" +
"ROLE_USER > ROLE_GUEST"
);
return roleHierarchy;
}
}
Permission-Based Authorization
Permission check
#auth permissions: ["read", "write"] {
#api /permission-protected {
return @permission_operation()
}
}Resource-specific permissions
#auth {
permissions: ["user:read", "user:write"]
resource: @request.params.user_id
} {
#api /user/{user_id} {
return @get_user_data(@request.params.user_id)
}
}Dynamic permissions
#auth {
permissions: () => {
return @get_user_permissions(@auth.user.id)
}
} {
#api /dynamic-permissions {
return @process_with_dynamic_permissions()
}
}
Java Permission-Based Authorization
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;@Service
public class PermissionBasedAuthService {
private final PermissionEvaluator permissionEvaluator;
private final UserPermissionService permissionService;
public PermissionBasedAuthService(PermissionEvaluator permissionEvaluator,
UserPermissionService permissionService) {
this.permissionEvaluator = permissionEvaluator;
this.permissionService = permissionService;
}
public boolean hasPermission(User user, String permission) {
return permissionService.hasPermission(user.getId(), permission);
}
public boolean hasPermission(User user, String permission, Object resource) {
return permissionEvaluator.hasPermission(
SecurityContextHolder.getContext().getAuthentication(),
resource,
permission
);
}
public boolean hasAnyPermission(User user, List<String> permissions) {
return permissions.stream().anyMatch(permission -> hasPermission(user, permission));
}
public boolean hasAllPermissions(User user, List<String> permissions) {
return permissions.stream().allMatch(permission -> hasPermission(user, permission));
}
public List<String> getUserPermissions(Long userId) {
return permissionService.getUserPermissions(userId);
}
}
@RestController
public class PermissionBasedController {
private final PermissionBasedAuthService authService;
private final UserService userService;
public PermissionBasedController(PermissionBasedAuthService authService,
UserService userService) {
this.authService = authService;
this.userService = userService;
}
@GetMapping("/api/user/{userId}")
@PreAuthorize("hasPermission(#userId, 'user:read')")
public ResponseEntity<UserResponse> getUser(@PathVariable Long userId,
@AuthenticationPrincipal User currentUser) {
if (!authService.hasPermission(currentUser, "user:read", userId)) {
return ResponseEntity.status(403).body(null);
}
return ResponseEntity.ok(userService.getUserById(userId));
}
@PutMapping("/api/user/{userId}")
@PreAuthorize("hasPermission(#userId, 'user:write')")
public ResponseEntity<UserResponse> updateUser(@PathVariable Long userId,
@RequestBody UserUpdateRequest request,
@AuthenticationPrincipal User currentUser) {
if (!authService.hasPermission(currentUser, "user:write", userId)) {
return ResponseEntity.status(403).body(null);
}
return ResponseEntity.ok(userService.updateUser(userId, request));
}
@GetMapping("/api/dynamic-permissions")
public ResponseEntity<List<String>> getDynamicPermissions(@AuthenticationPrincipal User user) {
List<String> permissions = authService.getUserPermissions(user.getId());
return ResponseEntity.ok(permissions);
}
}
@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
private final UserPermissionService permissionService;
public CustomPermissionEvaluator(UserPermissionService permissionService) {
this.permissionService = permissionService;
}
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
if (authentication == null || targetDomainObject == null || !(permission instanceof String)) {
return false;
}
User user = (User) authentication.getPrincipal();
String permissionString = (String) permission;
if (targetDomainObject instanceof Long) {
return permissionService.hasPermission(user.getId(), permissionString, (Long) targetDomainObject);
}
return permissionService.hasPermission(user.getId(), permissionString);
}
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
if (authentication == null || targetId == null || targetType == null || !(permission instanceof String)) {
return false;
}
User user = (User) authentication.getPrincipal();
String permissionString = (String) permission;
return permissionService.hasPermission(user.getId(), permissionString, (Long) targetId);
}
}
Multi-Tenant Authentication
Tenant-based authentication
#auth {
tenant: @request.headers.X-Tenant-ID
check_tenant: true
} {
#api /tenant-data {
return @get_tenant_data(@auth.tenant)
}
}Tenant isolation
#auth {
tenant: @auth.user.tenant
isolation: true
cross_tenant_access: false
} {
#api /isolated-data {
return @get_isolated_data()
}
}
Java Multi-Tenant Implementation
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;@Service
public class MultiTenantAuthService {
private final TenantService tenantService;
public MultiTenantAuthService(TenantService tenantService) {
this.tenantService = tenantService;
}
public String getCurrentTenant() {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null) {
HttpServletRequest request = attributes.getRequest();
String tenantId = request.getHeader("X-Tenant-ID");
if (tenantId != null) {
return tenantId;
}
}
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null && authentication.getPrincipal() instanceof User) {
User user = (User) authentication.getPrincipal();
return user.getTenant();
}
return null;
}
public boolean validateTenant(String tenantId) {
return tenantService.isValidTenant(tenantId);
}
public boolean hasTenantAccess(User user, String resourceTenant) {
// Check if user has access to the resource tenant
if (user.getTenant().equals(resourceTenant)) {
return true;
}
// Check for cross-tenant permissions
return user.hasPermission("cross_tenant:access") &&
user.hasPermission("tenant:" + resourceTenant + ":access");
}
}
@RestController
public class MultiTenantController {
private final MultiTenantAuthService tenantAuthService;
private final TenantDataService tenantDataService;
public MultiTenantController(MultiTenantAuthService tenantAuthService,
TenantDataService tenantDataService) {
this.tenantAuthService = tenantAuthService;
this.tenantDataService = tenantDataService;
}
@GetMapping("/api/tenant-data")
public ResponseEntity<TenantDataResponse> getTenantData(@AuthenticationPrincipal User user) {
String tenantId = tenantAuthService.getCurrentTenant();
if (tenantId == null || !tenantAuthService.validateTenant(tenantId)) {
return ResponseEntity.status(400).body(null);
}
if (!tenantAuthService.hasTenantAccess(user, tenantId)) {
return ResponseEntity.status(403).body(null);
}
return ResponseEntity.ok(tenantDataService.getTenantData(tenantId));
}
@GetMapping("/api/isolated-data")
public ResponseEntity<IsolatedDataResponse> getIsolatedData(@AuthenticationPrincipal User user) {
String userTenant = user.getTenant();
// Ensure data isolation
return ResponseEntity.ok(tenantDataService.getIsolatedData(userTenant));
}
}
OAuth2 Integration
OAuth2 authentication
#auth {
method: "oauth2"
provider: "google"
scopes: ["email", "profile"]
} {
#api /oauth-protected {
return @get_oauth_data()
}
}Multiple OAuth providers
#auth {
method: "oauth2"
providers: ["google", "github", "facebook"]
fallback: "session"
} {
#api /multi-oauth {
return @handle_multi_oauth()
}
}
Java OAuth2 Implementation
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;@Service
public class OAuth2AuthService {
private final OAuth2UserService oauth2UserService;
public OAuth2AuthService(OAuth2UserService oauth2UserService) {
this.oauth2UserService = oauth2UserService;
}
public OAuth2User getOAuth2User(Authentication authentication) {
if (authentication instanceof OAuth2AuthenticationToken) {
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
return oauthToken.getPrincipal();
}
return null;
}
public String getOAuth2Provider(Authentication authentication) {
if (authentication instanceof OAuth2AuthenticationToken) {
OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
return oauthToken.getAuthorizedClientRegistrationId();
}
return null;
}
public boolean hasOAuth2Scope(Authentication authentication, String scope) {
OAuth2User oauth2User = getOAuth2User(authentication);
if (oauth2User != null) {
return oauth2User.getAuthorities().stream()
.anyMatch(authority -> authority.getAuthority().equals("SCOPE_" + scope));
}
return false;
}
}
@RestController
public class OAuth2Controller {
private final OAuth2AuthService oauth2AuthService;
private final OAuth2DataService oauth2DataService;
public OAuth2Controller(OAuth2AuthService oauth2AuthService,
OAuth2DataService oauth2DataService) {
this.oauth2AuthService = oauth2AuthService;
this.oauth2DataService = oauth2DataService;
}
@GetMapping("/api/oauth-protected")
public ResponseEntity<OAuth2DataResponse> getOAuthData(@AuthenticationPrincipal OAuth2User oauth2User) {
String provider = oauth2AuthService.getOAuth2Provider(SecurityContextHolder.getContext().getAuthentication());
if (provider == null) {
return ResponseEntity.status(401).body(null);
}
return ResponseEntity.ok(oauth2DataService.getOAuthData(provider, oauth2User));
}
@GetMapping("/api/multi-oauth")
public ResponseEntity<String> handleMultiOAuth(@AuthenticationPrincipal OAuth2User oauth2User) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
String provider = oauth2AuthService.getOAuth2Provider(authentication);
if (provider == null) {
// Fallback to session authentication
return ResponseEntity.ok("Session authenticated");
}
return ResponseEntity.ok("OAuth2 authenticated via " + provider);
}
}
Authentication Testing
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.security.test.context.support.WithUserDetails;
import org.springframework.test.context.TestPropertySource;
import org.springframework.beans.factory.annotation.Autowired;@SpringBootTest
@TestPropertySource(properties = {
"spring.security.user.name=test",
"spring.security.user.password=test"
})
public class AuthTest {
@Autowired
private AuthenticatedController controller;
@Test
@WithMockUser(roles = "USER")
public void testUserAccess() {
ResponseEntity<DataResponse> response = controller.getProtectedData();
assertEquals(200, response.getStatusCodeValue());
}
@Test
@WithMockUser(roles = "ADMIN")
public void testAdminAccess() {
ResponseEntity<AdminDataResponse> response = controller.getAdminData();
assertEquals(200, response.getStatusCodeValue());
}
@Test
@WithMockUser(roles = "USER")
public void testUserAccessDenied() {
// This should be denied for USER role
ResponseEntity<AdminDataResponse> response = controller.getAdminData();
assertEquals(403, response.getStatusCodeValue());
}
@Test
public void testUnauthenticatedAccess() {
// This should redirect to login
ResponseEntity<String> response = controller.getSecurePage("invalid-token", null);
assertEquals(401, response.getStatusCodeValue());
}
}
Configuration Properties
application.yml
tusk:
auth:
default-method: "jwt"
default-roles: ["USER"]
default-permissions: ["READ"]
redirect-url: "/login"
error-code: 401
methods:
jwt:
enabled: true
secret: "your-secret-key-here"
expiration: 3600
issuer: "tusk-app"
session:
enabled: true
timeout: 1800
oauth2:
enabled: true
providers:
google:
client-id: "google-client-id"
client-secret: "google-client-secret"
scopes: ["email", "profile"]
github:
client-id: "github-client-id"
client-secret: "github-client-secret"
scopes: ["user:email"]
fallback-methods: ["session"]
priority-methods: ["jwt", "oauth2", "session"]spring:
security:
oauth2:
client:
registration:
google:
client-id: ${tusk.auth.methods.oauth2.providers.google.client-id}
client-secret: ${tusk.auth.methods.oauth2.providers.google.client-secret}
scope:
- email
- profile
github:
client-id: ${tusk.auth.methods.oauth2.providers.github.client-id}
client-secret: ${tusk.auth.methods.oauth2.providers.github.client-secret}
scope:
- user:email
Summary
The #auth
directive in TuskLang provides comprehensive authentication and authorization capabilities for Java applications. With Spring Security integration, JWT support, OAuth2 providers, and multi-tenant authentication, you can implement enterprise-grade security that scales with your application.
Key features include: - Multiple authentication methods: JWT, session, OAuth2, and API key authentication - Spring Security integration: Seamless integration with Spring Security framework - Role-based authorization: Hierarchical role system with inheritance - Permission-based authorization: Fine-grained permission control - Multi-tenant support: Tenant isolation and cross-tenant access control - OAuth2 integration: Support for multiple OAuth2 providers - JWT support: Token-based authentication with refresh tokens - Testing support: Comprehensive testing utilities with Spring Security Test
The Java implementation provides enterprise-grade authentication that integrates seamlessly with Spring Boot applications while maintaining the simplicity and power of TuskLang's declarative syntax.