001
002package io.vrap.rmf.base.client.oauth2;
003
004import java.util.concurrent.CompletableFuture;
005
006import io.vrap.rmf.base.client.AuthenticationToken;
007import io.vrap.rmf.base.client.http.InternalLogger;
008
009public class TokenStorageSupplier implements RefreshableTokenSupplier {
010    private final InternalLogger logger = InternalLogger.getLogger(LOGGER_AUTH);
011
012    private final TokenStorage storage;
013    private final AnonymousFlowTokenSupplier anonymousFlowTokenSupplier;
014    private final Object lock = new Object();
015
016    private volatile CompletableFuture<AuthenticationToken> tokenFuture;
017
018    public TokenStorageSupplier(TokenStorage storage, AnonymousFlowTokenSupplier supplier) {
019        this.storage = storage;
020        this.anonymousFlowTokenSupplier = supplier;
021    }
022
023    @Override
024    public CompletableFuture<AuthenticationToken> getToken() {
025        AuthenticationToken token = storage.getToken();
026        if (token != null && !token.isExpired()) {
027            return CompletableFuture.completedFuture(token);
028        }
029        if (token != null) {
030            if (token.getRefreshToken() != null) {
031                synchronized (lock) {
032                    if (tokenFuture == null) {
033                        logger.debug(() -> "using refresh token flow");
034                        tokenFuture = anonymousFlowTokenSupplier.refreshToken().thenApply(this::storeToken);
035                    }
036                }
037            }
038        }
039        synchronized (lock) {
040            if (tokenFuture == null) {
041                logger.debug(() -> "using anonymous token flow");
042                tokenFuture = anonymousFlowTokenSupplier.getToken().thenApply(this::storeToken);
043            }
044        }
045        return tokenFuture;
046    }
047
048    private void resetTokenFuture() {
049        synchronized (lock) {
050            tokenFuture = null;
051        }
052    }
053
054    private AuthenticationToken storeToken(AuthenticationToken token) {
055        storage.setToken(token);
056        resetTokenFuture();
057        return token;
058    }
059
060    @Override
061    public CompletableFuture<AuthenticationToken> refreshToken() {
062        resetTokenFuture();
063        AuthenticationToken token = storage.getToken();
064
065        if (token != null) {
066            synchronized (lock) {
067                if (tokenFuture == null && token.isExpired()) {
068                    logger.debug(() -> "refresh token");
069                    tokenFuture = anonymousFlowTokenSupplier.refreshToken().thenApply(this::storeToken);
070                }
071            }
072            return tokenFuture;
073        }
074
075        return getToken();
076    }
077}