Non-browser can't refresh token?

I have created an application, including the frontend and backend deployed separately using different ports, I get the token through the frontend page, in the backend to refresh the token reported 403, through the postman is also the same problem!

I followed the documentation exactly but it doesn’t work, the web page is able to refresh the token, it seems to be a cross-domain issue that I don’t quite understand!

the redirect is to the backend ?

front should not have the refresh. Otherwise it means your front has app secret which means potential security breach.

Process to acquire refresh and access tokens from front :

  • client request the app and is proposed to log ( eg http://mayapp/index )
  • front directs to backend oauth (eg http://myapp/login/oauth )
  • backend redirects to the oauth server (with requested scopes, app id, and state that is stored in session) ( https://login.eveonline.com/v2/oauth/authorize/ )
  • client logs in oauth server, ** is redirected to backend ** (eg myapp/login/code?code=xxx)
  • backend verifies that state is present in session, that code is valid, and exchanges code for access token + refresh token ( https://login.eveonline.com/v2/oauth/token with grant_type=authorization_code ) . Saves the refresh token if needed.
  • backed also saves the access token along with expiry to reuse it

Later the backend needs to call ESI with user access :

  • check if exists valid access token with required scopes (and roles)
  • if not, check if exists refresh token
    • if not, can’t access the resource
    • if yes, request a new access token to oauth server, from existing refresh token. Also if returned refresh token is different, save it instead of previous one. Save new access token along with expiry.

When front needs resource :

  • basically backend works as proxy, uses client session to know of his character(s)
  • can also store the result as a cache.

sir,I did exactly as you instructed, front-end request, back-end redirect to /v2/oauth/authorize/?response_type=code&redirect_uri=https%3A%2F%2Flocalhost%2Fcallback%2F&client_id=&scope=esi-characters.read_blueprints.v1&state=.

The callback address is my backend interface to make remote calls in the callback interface: /v2/oauth/token

I set the header:
Authorization: Basic <Base64 encoded credentials.
Content-Type: application/x-www-form-urlencoded
Host: login.eveonline.com

Also passed parameters:
grant_type=authorization_code&code=

But I am not allowed to access it, here is the text/html content returned :

Attention Required! | Cloudflarebody{margin:0;padding:0}
Please enable cookies.

Sorry, you have been blocked

You are unable to access eveonline.com

Why have I been blocked?

This website is using a security service to protect itself from online attacks. The action you just performed triggered the security solution. There are several actions that could trigger this block including submitting a certain word or phrase, a SQL command or malformed data.

What can I do to resolve this?

You can email the site owner to let them know you were blocked. Please include what you were doing when this page came up and the Cloudflare Ray ID found at the bottom of this page.

Cloudflare Ray ID: 89c2d0fc2ace2119 Your IP: Click to reveal Performance & security by Cloudflare

I’ve looked up some information about Cloudflare returning a 403 error, and I’m not sure if the application was determined to be a crawler, causing it to trigger User-Agent detection, and the back-end application is written in Java, but if that’s the case, how do I fix the problem?

I have no idea either.

What java stack are you using for backend ?

spring framework

how do you configure ?

Here is my properties for the provider

spring.security.oauth2.client.provider.ccplz.authorizationGrantType=authorization_code
spring.security.oauth2.client.provider.ccplz.authorizationUri=https://login.eveonline.com/oauth/authorize
spring.security.oauth2.client.provider.ccplz.issuerUri=https://login.eveonline.com
spring.security.oauth2.client.provider.ccplz.tokenUri=https://login.eveonline.com/oauth/token
spring.security.oauth2.client.provider.ccplz.userInfoUri=https://login.eveonline.com/oauth/verify
spring.security.oauth2.client.provider.ccplz.userNameAttribute=CharacterName

You should replace with v2/ but in my case I have legacy apps so I keep this one.

and for the scopes management :

spring.security.oauth2.client.registration.eve1.clientId=XYZ
spring.security.oauth2.client.registration.eve1.clientName=empty access
spring.security.oauth2.client.registration.eve1.clientSecret=***
spring.security.oauth2.client.registration.eve1.provider=ccplz
spring.security.oauth2.client.registration.eve1.scope=publicData

spring.security.oauth2.client.registration.wallet.clientId=XYZ
spring.security.oauth2.client.registration.wallet.clientName=wallet       
spring.security.oauth2.client.registration.wallet.clientSecret=***
spring.security.oauth2.client.registration.wallet.provider=ccplz
spring.security.oauth2.client.registration.wallet.redirectUri=http://myapp/login/oauth2/code/redirect
spring.security.oauth2.client.registration.wallet.scope=esi-wallet.read_character_wallet.v1

This allows the client to choose among two sets of scopes depending on what he want to authorize the refresh token.

also a problem with spring is that the refresh token is not standard in oauth, so you need a custom refresh token coverter like

public class CustomTokenResponseConverter implements
    Converter<Map<String, Object>, OAuth2AccessTokenResponse> {

	@Override
	public OAuth2AccessTokenResponse convert(Map<String, Object> tokenResponseParameters) {
		try {
			String accessToken = (String) tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);
			String refreshToken = (String) tokenResponseParameters.get(OAuth2ParameterNames.REFRESH_TOKEN);
			Object expiresInObj = tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN);
			long expiresIn = ((Number) expiresInObj).longValue();

			Set<String> scopes = Collections.emptySet();
			if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
				String scope = (String) tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
				scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, " "))
				    .collect(Collectors.toSet());
			}

			return OAuth2AccessTokenResponse.withToken(accessToken)
			    .tokenType(OAuth2AccessToken.TokenType.BEARER)
			    .expiresIn(expiresIn)
			    .scopes(scopes)
			    .refreshToken(refreshToken)
			    .additionalParameters(Map.of(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken))
			    .build();
		} catch (Exception e) {
			log.error("while converting parameters " + tokenResponseParameters, e);
			return null;
		}
	}
}

That you register in you @autoconfiguration class like


	@Bean
	public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {

		DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();

		OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
		tokenResponseHttpMessageConverter.setAccessTokenResponseConverter(new CustomTokenResponseConverter());

		RestTemplate restTemplate = new RestTemplate(Arrays.asList(
		    new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
		restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
		accessTokenResponseClient.setRestOperations(restTemplate);

		return accessTokenResponseClient;
	}

There is another issue that the scopes are not translated to authorities so you need to extend the DefaultOAuth2UserService::loadUser if you want to save the refresh token

bro, I solved this problem!
Reason: Cloudflare’s web application firewall blocks receiving requests with the User-Agent header Java/any version.

Thanks for your efforts!

Seems like you are not using spring to manage the refresh and access tokens.

yes, i’m not using the spring security