Skip to main content

spring_security_core 0.4.2 copy "spring_security_core: ^0.4.2" to clipboard
spring_security_core: ^0.4.2 copied to clipboard

Spring Security for Dart: the security context, users, password encoders (Argon2, PBKDF2, scrypt) and method security with @PreAuthorize on bean methods.

spring_security_core #

Spring Security's core for Dart: who is logged in, users and passwords, and method security — @PreAuthorize, @PostAuthorize, @Secured, @PreFilter and @PostFilter on bean methods. It works on a server (with spring_boot_starter_security, which brings it) and in a Flutter app.

dependencies:
  spring_security_core: ^0.4.2

It brings and re-exports spring_boot_dart and the logging starter: an application lists it (or the security starter) alone.

In the guides: server, 4. Security, app, 7. Login & roles.

Method security #

@Service()
class OrderService {
  @PreAuthorize("hasRole('ADMIN') or #order.owner == principal.username")
  Future<void> cancel(Order order) async { ... }

  @PostAuthorize('returnObject.owner == authentication.name')
  Future<Order> find(String id) => orders.load(id);

  @Secured(['ROLE_SUPPORT'])
  Future<void> refund(String id) async { ... }

  @PostFilter('filterObject.owner == authentication.name')
  Future<List<Order>> all() => orders.all();
}

A denied call throws AccessDeniedException, a call without any authentication AuthenticationCredentialsNotFoundException. An async method fails through its Future, before its body runs. The package knows nothing of HTTP (it depends only on spring_boot_dart and the logging starter): in a web application spring_boot_starter_security answers these exceptions 403 and 401.

In the expression Is
hasRole('ADMIN'), hasAnyRole('A', 'B') the user has ROLE_ADMIN / one of the roles (the RoleHierarchy bean applies)
hasAuthority('SCOPE_read'), hasAnyAuthority(...) the user has the authority
isAuthenticated(), isFullyAuthenticated(), isAnonymous() how the user logged in
permitAll, denyAll always / never
principal, authentication the user (principal.username), the Authentication (authentication.name)
#order, #p0, #a0 the arguments
returnObject, filterObject the result (@PostAuthorize), the element (@PreFilter, @PostFilter)
hasPermission(#order, 'cancel') the PermissionEvaluator bean (denies without one)
@shop.isOpen() a bean (its class @Introspected)

Classes of the bean's own package used by the method (Order) are readable in expressions without @Introspected.

Like Spring, the checks run through the bean's proxy: a call from inside the bean (this.cancel(...)) is not checked. Unlike Spring, nothing has to enable method security (@EnableMethodSecurity): the dependency is enough, so an annotation is never silently ignored — and a bean with such annotations that cannot be proxied fails the startup instead of running unchecked. Every expression is parsed at startup. Method security runs before every other advice: a @Cacheable result is never returned to a caller who may not call the method.

Who is logged in #

final authentication = SecurityContextHolder.getContext().authentication;

The context is kept per Zone (Dart's ThreadLocal): on a server the security filter chain gives each request its own, so a request sees only its user, also after await. Outside every scope there is one context for the whole program — in a Flutter app, the logged-in user:

final result = await authenticationManager.authenticate(
  UsernamePasswordAuthenticationToken.unauthenticated(name, password),
);
SecurityContextHolder.setContext(SecurityContextImpl(result));

SecurityContextHolder.runWithContext(body, context) runs work as a given user (a job, a test).

Users and passwords #

Type Like Spring's
UserDetails, User.withUsername(...).password(...).roles([...]).build() User
UserDetailsService, InMemoryUserDetailsManager the same
AuthenticationManager, ProviderManager, DaoAuthenticationProvider the same
PasswordEncoderFactories.createDelegatingPasswordEncoder() the same, Argon2 by default
Argon2PasswordEncoder, Pbkdf2PasswordEncoder, SCryptPasswordEncoder the same, Spring's formats and defaults

With a UserDetailsService bean (and a PasswordEncoder bean, or the delegating encoder) the auto-configuration builds the AuthenticationManager.

DaoAuthenticationProvider, like Spring's:

  • an unknown user and a wrong password fail the same way (BadCredentialsException), and an unknown user still checks the password against a dummy hash, so the time does not tell which;
  • locked and disabled accounts fail before the password check, an expired password after it;
  • an old hash (another {id}, weaker parameters) is re-hashed at the next login through a UserDetailsPasswordService.

Password hashing takes hundreds of milliseconds of CPU: here it runs on another isolate, so a login does not stop the server's other requests.

Events: AuthenticationSuccessEvent and the failure events (AuthenticationFailureBadCredentialsEvent, ...) — listen to them to lock accounts or slow clients down; the packages do not count attempts.

Tests #

import 'package:spring_security_core/testing.dart';

test('admins cancel any order', () async {
  await withMockUser(() => orders.cancel(order), roles: ['ADMIN']);
});

withAuthentication(body, authentication) and withAnonymousUser(body) too.

Differences from Spring Security #

  • BCrypt (Spring's default {bcrypt}) is not available: there is no vetted Dart implementation, and cryptography is not written here by hand (docs/security.md). New hashes are {argon2} (which Spring checks too); a {bcrypt} hash fails with an error saying so. Argon2, PBKDF2 and scrypt use Bouncy Castle's Dart port (pointycastle, a third-party package to be replaced), like Spring's Java encoders, and their hashes move between Java and Dart both ways (checked against Spring Security 7.1 by tool/security_reference).
  • Method security is always on (no @EnableMethodSecurity), and unproxied annotated beans fail the startup.
  • Beans called from expressions (@shop.isOpen()) need @Introspected (no reflection).
  • Multi-factor authorities: Spring Security 7 adds FACTOR_PASSWORD to users who logged in with a password; not here yet.

How the packages are shown to be safe: docs/security.md and roadmap/security_verification.md.

0
likes
0
points
239
downloads

Publisher

verified publisherfarum-azula.org

Weekly Downloads

Spring Security for Dart: the security context, users, password encoders (Argon2, PBKDF2, scrypt) and method security with @PreAuthorize on bean methods.

Repository (GitHub)
View/report issues

Topics

#security #authentication #authorization #spring

License

unknown (license)

Dependencies

pointycastle, spring_boot_dart, spring_boot_starter_logging

More

Packages that depend on spring_security_core