I configured an Hydra instance with my Spring Boot app. I just configured my app as a resource server using the annotation @EnableResourceServer
. So, when I'm using the Bearer authorization header in my request, Spring uses the value that I specify in the property:
security.oauth2.resource.user-info-uri=...
To validate if the token is valid or not. Unfortunately, I don't find what's this URL with Hydra OAuth 2.0 (http://docs.hydra13.apiary.io/ / https://github.com/ory/hydra)
First: Configure your resource server in Ory Hydra (you must add it to Ory Hydra with client_credentials and scope 'hydra.introspect' in order to be able to ask for token validity):
$> hydra clients create --skip-tls-verify \
--id my-rest-api \
--secret mypwd \
--grant-types client_credentials \
--response-types token \
--allowed-scopes hydra.introspect
Second: Add a policy to let your resource server ask for token validity.
$> hydra policies create --skip-tls-verify \
--actions introspect \
--description "Policy to introspect tokens from my api" \
--allow \
--id accesstoken_introsp-policy \
--resources "rn:hydra:oauth2:tokens" \
--subjects my-rest-api
Third: Add oauth2 dependency in build.gradle (or pom.xml if its maven):
compile 'org.springframework.security.oauth:spring-security-oauth2:2.2.1.RELEASE'
Fourth: Configure application.yml to use Ory Hydra introspection endpoint to get token info.
security:
user:
password: none
oauth2:
resource:
token-info-uri: https://yourserver.com/oauth2/introspect
client:
client-id: my-rest-api
client-secret: mypwd
scope: [ "hydra.introspect" ]
Fifth: Create a class to configure urls protected by access tokens
@Configuration
@EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Autowired
private RemoteTokenServices tokenServices;
@Value("${security.oauth2.client.client-id}")
private String clientId;
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.NEVER)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/api/v1/**").access("#oauth2.hasScope('my.desired.scope')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(clientId);
tokenServices.setAccessTokenConverter(new OryHydraAccessTokenConverter());
resources.tokenServices(tokenServices);
}
}
class OryHydraAccessTokenConverter extends DefaultAccessTokenConverter {
@Override
public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
OAuth2Authentication oAuth2Authentication = super.extractAuthentication(map);
oAuth2Authentication.setDetails(map.get("ext"));
return oAuth2Authentication;
}
}
I need a custom AccessTokenConverter because my consent app add several properties to the token and we need to map all of them. With Ory, my properties are under 'ext' property. Here's an example access token:
{
"active": true,
"scope": "my.desired.scope",
"client_id": "my-mobile-app",
"sub": "123121e",
"exp": 1520948372,
"iat": 1520944772,
"iss": "https://yourserver.com",
"ext": {
"custom_prop1": 12321,
"custom_prop2": "Name Surname",
"custom_prop3": false
}
}
Last step: Now in your controllers you can autowire as parameter Oauth2Authentication object.
@GetMapping("/api/v1/data")
public MyBean findDataById(OAuth2Authentication auth,
@RequestParam("id") String id) {
OAuth2AuthenticationDetails oAuth2AuthenticationDetails = (OAuth2AuthenticationDetails) auth.getDetails();
Map<String, Object> ext = (Map<String, Object>) oAuth2AuthenticationDetails.getDecodedDetails();
return MyBean.builder().name("Name:"+ext.get("custom_prop1")).build();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With