class OidcProfileCreator extends ProfileDefinitionAware implements ProfileCreator {

    protected OidcConfiguration configuration;

    protected OidcClient client;

    /**
     * <p>Constructor for OidcProfileCreator.</p>
     *
     * @param configuration a {@link OidcConfiguration} object
     * @param client a {@link OidcClient} object
     */
    public OidcProfileCreator(final OidcConfiguration configuration, final OidcClient client) {
        this.configuration = configuration;
        this.client = client;
    }

    /** {@inheritDoc} */
    @Override
    protected void internalInit(final boolean forceReinit) {
        assertNotNull("configuration", configuration);

        setProfileDefinitionIfUndefined(new OidcProfileDefinition());
    }

    /** {@inheritDoc} */
    @Override
    @SuppressWarnings("unchecked")
    public Optional<UserProfile> create(final CallContext ctx, final Credentials credentials) {<FILL_FUNCTION_BODY>}

    public void callUserInfoEndpoint(final URI userInfoEndpointUri, final AccessToken accessToken, final UserProfile profile)
        throws IOException, ParseException, java.text.ParseException, UserInfoErrorResponseException {
        val opMetadata = configuration.getOpMetadataResolver().load();
        if (opMetadata.getUserInfoEndpointURI() != null && accessToken != null) {
            Request userInfoRequest = new UserInfoRequest(opMetadata.getUserInfoEndpointURI(), accessToken);
            val userInfoHttpRequest = userInfoRequest.toHTTPRequest();
            configuration.configureHttpRequest(userInfoHttpRequest);
            val httpResponse = userInfoHttpRequest.send();
            LOGGER.debug("User info response: status={}, content={}", httpResponse.getStatusCode(),
                httpResponse.getContent());

            val userInfoResponse = UserInfoResponse.parse(httpResponse);
            if (userInfoResponse instanceof UserInfoErrorResponse) {
                final var error = ((UserInfoErrorResponse) userInfoResponse).getErrorObject();
                LOGGER.error("Bad User Info response, error={}", error);

                throw new UserInfoErrorResponseException(error.toString());
            } else {
                val userInfoSuccessResponse = (UserInfoSuccessResponse) userInfoResponse;
                final JWTClaimsSet userInfoClaimsSet;
                if (userInfoSuccessResponse.getUserInfo() != null) {
                    userInfoClaimsSet = userInfoSuccessResponse.getUserInfo().toJWTClaimsSet();
                } else {
                    userInfoClaimsSet = userInfoSuccessResponse.getUserInfoJWT().getJWTClaimsSet();
                }
                if (userInfoClaimsSet != null) {
                    getProfileDefinition().convertAndAdd(profile, userInfoClaimsSet.getClaims(), null);
                } else {
                    LOGGER.warn("Cannot retrieve claims from user info");
                }
            }
        }
    }

    private void collectClaimsFromAccessTokenIfAny(final OidcCredentials credentials,
                                                   final Nonce nonce, UserProfile profile) {
        try {
            var accessToken = credentials.toAccessToken();
            if (accessToken != null) {
                var accessTokenJwt = JWTParser.parse(accessToken.getValue());
                var accessTokenClaims = configuration.getOpMetadataResolver().getTokenValidator().validate(accessTokenJwt, nonce);

                // add attributes of the access token if they don't already exist
                for (val entry : accessTokenClaims.toJWTClaimsSet().getClaims().entrySet()) {
                    val key = entry.getKey();
                    val value = entry.getValue();
                    if (!JwtClaims.SUBJECT.equals(key) && profile.getAttribute(key) == null) {
                        getProfileDefinition().convertAndAdd(profile, PROFILE_ATTRIBUTE, key, value);
                    }
                }
            }
        } catch (final ParseException | java.text.ParseException | JOSEException | BadJOSEException e) {
            LOGGER.debug(e.getMessage(), e);
        } catch (final Exception e) {
            throw new OidcException(e);
        }
    }
}

        init();

        OidcCredentials oidcCredentials = null;
        AccessToken accessToken = null;

        val regularOidcFlow = credentials instanceof OidcCredentials;
        if (regularOidcFlow) {
            oidcCredentials = (OidcCredentials) credentials;
            accessToken = oidcCredentials.toAccessToken();
        } else {
            // we assume the access token only has been passed: it can be a bearer call (HTTP client)
            val token = ((TokenCredentials) credentials).getToken();
            accessToken = new BearerAccessToken(token);
        }

        // Create profile
        val profile = (OidcProfile) getProfileDefinition().newProfile();
        profile.setAccessToken(accessToken);

        if (oidcCredentials != null) {
            if (oidcCredentials.getIdToken() != null) {
                profile.setIdTokenString(oidcCredentials.toIdToken().getParsedString());
            }
            // Check if there is a refresh token
            val refreshToken = oidcCredentials.toRefreshToken();
            if (refreshToken != null && !refreshToken.getValue().isEmpty()) {
                profile.setRefreshToken(refreshToken);
                LOGGER.debug("Refresh Token successful retrieved");
            }
        }

        try {

            final Nonce nonce;
            if (configuration.isUseNonce()) {
                nonce = new Nonce((String) ctx.sessionStore().get(ctx.webContext(), client.getNonceSessionAttributeName()).orElse(null));
            } else {
                nonce = null;
            }
            // Check ID Token
            if (oidcCredentials != null && oidcCredentials.getIdToken() != null) {
                val claimsSet = configuration.getOpMetadataResolver().getTokenValidator().validate(oidcCredentials.toIdToken(), nonce);
                assertNotNull("claimsSet", claimsSet);
                profile.setId(ProfileHelper.sanitizeIdentifier(claimsSet.getSubject()));

                // keep the session ID if provided
                val sid = (String) claimsSet.getClaim(Pac4jConstants.OIDC_CLAIM_SESSIONID);
                val sessionLogoutHandler = client.findSessionLogoutHandler();
                if (isNotBlank(sid) && sessionLogoutHandler != null) {
                    sessionLogoutHandler.recordSession(ctx, sid);
                }
            }

            if (configuration.isCallUserInfoEndpoint()) {
                final var uri = configuration.getOpMetadataResolver().load().getUserInfoEndpointURI();
                try {
                    callUserInfoEndpoint(uri, accessToken, profile);
                } catch (final UserInfoErrorResponseException e) {
                    // bearer call -> no profile returned
                    if (!regularOidcFlow) {
                        return Optional.empty();
                    }
                }
            }

            // add attributes of the ID token if they don't already exist
            if (oidcCredentials != null && oidcCredentials.getIdToken() != null) {
                for (val entry : oidcCredentials.toIdToken().getJWTClaimsSet().getClaims().entrySet()) {
                    val key = entry.getKey();
                    val value = entry.getValue();
                    // it's not the subject and this attribute does not already exist, add it
                    if (!JwtClaims.SUBJECT.equals(key) && profile.getAttribute(key) == null) {
                        getProfileDefinition().convertAndAdd(profile, PROFILE_ATTRIBUTE, key, value);
                    }
                }
            }

            if (oidcCredentials != null && configuration.isIncludeAccessTokenClaimsInProfile()) {
                collectClaimsFromAccessTokenIfAny(oidcCredentials, nonce, profile);
            }

            // session expiration with token behavior
            profile.setTokenExpirationAdvance(configuration.getTokenExpirationAdvance());

            return Optional.of(profile);
        } catch (final IOException | ParseException | JOSEException | BadJOSEException | java.text.ParseException e) {
            throw new OidcException(e);
        }
    
<methods>public non-sealed void <init>() ,public org.pac4j.core.profile.definition.ProfileDefinition getProfileDefinition() ,public void setProfileDefinition(org.pac4j.core.profile.definition.ProfileDefinition) <variables>private org.pac4j.core.profile.definition.ProfileDefinition profileDefinition