diff --git a/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java b/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java index 8ba0b0fe..cbb9867c 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManager.java @@ -308,25 +308,35 @@ private void dropCurrentToken() { public Mono getVaultToken() throws VaultException { Mono tokenWrapper = this.token.get(); if (tokenWrapper == EMPTY) { - Mono obtainToken = this.clientAuthentication.getVaultToken() - .flatMap(this::doSelfLookup) // - .onErrorMap(it -> { - multicastEvent(new LoginFailedEvent(this.clientAuthentication, it)); - return it; - }) - .doOnNext(it -> { - if (isTokenRenewable(it.getToken())) { - scheduleRenewal(it.getToken()); - } - multicastEvent(new AfterLoginEvent(it.getToken())); - }); - - this.token.compareAndSet(tokenWrapper, obtainToken.cache()); + Mono obtainToken = createLoginTokenMono(); + this.token.compareAndSet(tokenWrapper, obtainToken); } return this.token.get().map(TokenWrapper::getToken); } + private Mono createLoginTokenMono() { + AtomicReference> cachedLoginRef = new AtomicReference<>(EMPTY); + Mono obtainToken = this.clientAuthentication.getVaultToken() + .flatMap(this::doSelfLookup) // + .onErrorMap(it -> { + multicastEvent(new LoginFailedEvent(this.clientAuthentication, it)); + return it; + }) + .doOnNext(it -> { + if (isTokenRenewable(it.getToken())) { + scheduleRenewal(it.getToken()); + } + multicastEvent(new AfterLoginEvent(it.getToken())); + }) + .doOnError(it -> this.token.compareAndSet(cachedLoginRef.get(), EMPTY)) + .cache(); + + cachedLoginRef.set(obtainToken); + + return obtainToken; + } + private Mono doSelfLookup(VaultToken token) { TokenWrapper wrapper = new TokenWrapper(token, token instanceof LoginToken); if (isTokenSelfLookupEnabled() && !ClassUtils.isAssignableValue(LoginToken.class, token)) { diff --git a/spring-vault-core/src/test/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManagerUnitTests.java b/spring-vault-core/src/test/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManagerUnitTests.java index 76f7e37e..4d9d2147 100644 --- a/spring-vault-core/src/test/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManagerUnitTests.java +++ b/spring-vault-core/src/test/java/org/springframework/vault/authentication/ReactiveLifecycleAwareSessionManagerUnitTests.java @@ -147,6 +147,26 @@ void loginShouldFail() { verify(this.errorListener).onAuthenticationError(any(LoginFailedEvent.class)); } + @Test + void shouldRetryLoginAfterFailure() { + + when(this.tokenSupplier.getVaultToken()).thenReturn(Mono.error(new VaultLoginException("foo")), + Mono.just(LoginToken.of("login"))); + + this.sessionManager.getSessionToken() // + .as(StepVerifier::create) // + .verifyError(VaultLoginException.class); + + this.sessionManager.getSessionToken() // + .as(StepVerifier::create) // + .expectNext(LoginToken.of("login")) // + .verifyComplete(); + + verify(this.tokenSupplier, times(2)).getVaultToken(); + verify(this.errorListener).onAuthenticationError(any(LoginFailedEvent.class)); + verify(this.listener).onAuthenticationEvent(any(AfterLoginEvent.class)); + } + @Test @SuppressWarnings("unchecked") void shouldSelfLookupToken() {