All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.twitch4j.helix.interceptor.TwitchHelixTokenManager Maven / Gradle / Ivy

The newest version!
package com.github.twitch4j.helix.interceptor;

import com.github.philippheuer.credentialmanager.CredentialManager;
import com.github.philippheuer.credentialmanager.domain.OAuth2Credential;
import com.github.twitch4j.auth.providers.TwitchIdentityProvider;
import io.github.xanthic.cache.api.Cache;
import io.github.xanthic.cache.api.domain.ExpiryType;
import io.github.xanthic.cache.core.CacheApi;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.time.Duration;
import java.util.Objects;

@Slf4j
public final class TwitchHelixTokenManager {

    private static final String CLIENT_ID_CONTEXT = "client_id";

    /**
     * Access token cache
     */
    private final Cache accessTokenCache = CacheApi.create(spec -> {
        spec.maxSize(10_000L);
        spec.expiryType(ExpiryType.POST_ACCESS);
        spec.expiryTime(Duration.ofMinutes(15L));
    });

    /**
     * Reference to the twitch identity provider
     */
    private final TwitchIdentityProvider twitchIdentityProvider;

    /**
     * The client id provided in the helix builder
     */
    private final String clientId;

    /**
     * The client secret provided in the helix builder
     */
    private final String clientSecret;

    /**
     * The client id associated with defaultAuthToken
     */
    @Getter(AccessLevel.PACKAGE)
    private volatile String defaultClientId;

    /**
     * The default token that is used if no access token was directly provided to a helix call
     */
    private volatile OAuth2Credential defaultAuthToken;

    @ApiStatus.Internal
    public TwitchHelixTokenManager(CredentialManager credentialManager, String clientId, String clientSecret, OAuth2Credential defaultAuthToken) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.twitchIdentityProvider = credentialManager.getIdentityProviderByName(TwitchIdentityProvider.PROVIDER_NAME, TwitchIdentityProvider.class)
            .orElseGet(() -> new TwitchIdentityProvider(clientId, clientSecret, null));
        this.defaultClientId = clientId;
        this.defaultAuthToken = defaultAuthToken;

        if (defaultAuthToken != null) {
            this.defaultClientId = Objects.toString(defaultAuthToken.getContext().getOrDefault(CLIENT_ID_CONTEXT, clientId), null);
            twitchIdentityProvider.getAdditionalCredentialInformation(defaultAuthToken).ifPresent(oauth -> {
                populateCache(oauth);
                this.defaultClientId = extractClientId(oauth);
                this.defaultAuthToken.updateCredential(oauth);
            });
        }
    }

    OAuth2Credential getDefaultAuthToken() {
        if (defaultAuthToken == null) {
            synchronized (this) {
                if (defaultAuthToken == null) {
                    checkClientCredentialsParameters();
                    OAuth2Credential token = twitchIdentityProvider.getAppAccessToken();
                    populateCache(token);
                    this.defaultClientId = (String) token.getContext().computeIfAbsent(CLIENT_ID_CONTEXT, s -> clientId);
                    return this.defaultAuthToken = token;
                }
            }
        }

        return defaultAuthToken;
    }

    @Nullable
    OAuth2Credential getIfPresent(@NotNull String accessToken) {
        return accessTokenCache.get(accessToken);
    }

    @NotNull
    OAuth2Credential getOrPopulateCache(@NotNull String accessToken) {
        OAuth2Credential verifiedCredential = getIfPresent(accessToken);

        if (verifiedCredential == null) {
            log.debug("Getting matching client-id for authorization token {}", accessToken.substring(0, 5));
            verifiedCredential = twitchIdentityProvider
                .getAdditionalCredentialInformation(new OAuth2Credential(twitchIdentityProvider.getProviderName(), accessToken))
                .orElseThrow(() -> new RuntimeException("Failed to get the client_id for the provided authentication token, the authentication token may be invalid!"));
            populateCache(verifiedCredential);
        }

        return verifiedCredential;
    }

    private void populateCache(@NotNull OAuth2Credential enrichedCredential) {
        accessTokenCache.put(enrichedCredential.getAccessToken(), enrichedCredential);
    }

    private void checkClientCredentialsParameters() {
        if (StringUtils.isEmpty(defaultClientId) || StringUtils.isEmpty(clientSecret) || clientSecret.charAt(0) == '*')
            throw new RuntimeException("Necessary OAuth token was missing from Helix call, without the means to generate one!");
    }

    static String extractClientId(@NotNull OAuth2Credential credential) {
        return (String) credential.getContext().get(CLIENT_ID_CONTEXT);
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy