class StatefulTokenStore implements OpenIdConnectTokenStore {

    public DeviceCode createDeviceCode(Set<String> scope, ResourceOwner resourceOwner, String clientId, String nonce,
            String responseType, String state, String acrValues, String prompt, String uiLocales, String loginHint,
            Integer maxAge, String claims, OAuth2Request request, String codeChallenge, String codeChallengeMethod)
            throws ServerException, NotFoundException {

        logger.message("DefaultOAuthTokenStoreImpl::Creating Authorization code");

        final OAuth2ProviderSettings providerSettings = providerSettingsFactory.get(request);
        final String deviceCode = UUID.randomUUID().toString();
        final String auditId = IdGenerator.DEFAULT.generate();

        String userCode = null;

        int i;
        for (i = 0; i < NUM_RETRIES; i++) {

            String result = recoveryCodeGenerator.generateCode(Alphabet.BASE58, CODE_LENGTH);

            try {
                readDeviceCode(result, request);
            } catch (InvalidGrantException e) {
                // Good, it doesn't exist yet.
                userCode = result;
                break;
            } catch (ServerException e) {
                logger.message("Could not query CTS, assume duplicate to be safe", e);
            }
        }

        if (i == NUM_RETRIES) {
            throw new ServerException("Could not generate a unique user code");
        }

        String realm;
        try {
            realm = realmNormaliser.normalise(request.<String>getParameter(REALM));
        } catch (org.forgerock.json.resource.NotFoundException e) {
            throw new NotFoundException(e.getMessage());
        }

        long expiryTime = currentTimeMillis() + (1000 * providerSettings.getDeviceCodeLifetime());
        String resourceOwnerId = resourceOwner == null ? null : resourceOwner.getId();
        String storedClaims = providerSettings.getClaimsParameterSupported() ? claims : null;
        final DeviceCode code = new DeviceCode(deviceCode, userCode, resourceOwnerId, clientId, nonce,
                responseType, state, acrValues, prompt, uiLocales, loginHint, maxAge, storedClaims, expiryTime, scope,
                realm, codeChallenge, codeChallengeMethod, auditId);

        // Store in CTS
        try {
            tokenStore.create(code);
            if (auditLogger.isAuditLogEnabled()) {
                String[] obs = {"CREATED_DEVICE_CODE", code.toString()};
                auditLogger.logAccessMessage("CREATED_DEVICE_CODE", obs, null);
            }
        } catch (CoreTokenException e) {
            if (auditLogger.isAuditLogEnabled()) {
                String[] obs = {"FAILED_CREATE_DEVICE_CODE", code.toString()};
                auditLogger.logErrorMessage("FAILED_CREATE_DEVICE_CODE", obs, null);
            }
            logger.error("Unable to create device code " + code, e);
            throw new ServerException("Could not create token in CTS");
        }

        request.setToken(DeviceCode.class, code);

        return code;
    }

    @Inject public  StatefulTokenStore(OAuthTokenStore tokenStore, OAuth2ProviderSettingsFactory providerSettingsFactory,
            OAuth2UrisFactory<RealmInfo> oauth2UrisFactory,
            OpenIdConnectClientRegistrationStore clientRegistrationStore, RealmNormaliser realmNormaliser,
            SSOTokenManager ssoTokenManager, CookieExtractor cookieExtractor, OAuth2AuditLogger auditLogger,
            @Named(OAuth2Constants.DEBUG_LOG_NAME) Debug logger, SecureRandom secureRandom,
            ClientAuthenticationFailureFactory failureFactory, RecoveryCodeGenerator recoveryCodeGenerator);

}