You are on page 1of 7

API Security using OIDC

Table of Contents
API Security using OIDC..............................................................................................................................1
1. Create and configure and API Oauth Client in OKTA......................................................................2
2. Configure resource server to protect API resources using OKTA...................................................2
SpringBoot example :..............................................................................................................................2
i) Maven Dependencies to enable Oauth2 Resource server using OKTA OIDC client:.....................2
iv) Test your application:.................................................................................................................3
3. Using different types of Token Validations:...................................................................................3
i) JWT token validation (Local):......................................................................................................4
Note : When using PingFederate as Authorization server.................................................................4
ii) Using Token introspection endpoint (Remote):.........................................................................4
iii) Using UserInfo endpoint (Remote):........................................................................................5
Extract Username Attribute........................................................................................................................5
API Access Management............................................................................................................................5
1. Restrict access to API based on ‘Scope’:.........................................................................................5
2. Restrict access to API based on ‘ClientID’:......................................................................................5
3. Restrict access to API based on Username/Principal attribute :....................................................6
4. Restrict API access based on User’s group membership :..............................................................6
Method 2 : Another method is explained at...........................................................................................6
5. Add Scope/Claim based access on Individual API :.......................................................................7

Please follow below steps to secure an API resource server using OKTA.

1. Create and configure and API Oauth Client in OKTA


Configure an Oauth client of API(Resource Server) Type using OKTA admin console. API
client will use only client_credential Grant type. Save the ClientID and ClientSecret which
will be needed for further configuration.
Follow below documents to setup the API security using OKTA admin console and configure
you Resource(API) server using OKTA
- Okta documentation on how to create API client_credential clients using OKTA admin
console
https://developer.okta.com/docs/guides/implement-grant-type/clientcreds/main/

2. Configure resource server to protect API resources using OKTA


Use your programming language guide to add OKTA as Oauth provider to protect the
resource server.

- Follow below documentation to setup and protect your resource server using OKTA

https://developer.okta.com/docs/guides/protect-your-api/aspnet/before-you-begin/

https://github.com/okta/samples-java-spring/tree/master/resource-server

SpringBoot example : Below is example to configure resource server Using SpringBoot

Sample application is also available at github alongwith documentation


https://github.com/okta/samples-java-spring/tree/master/resource-server

i) Maven Dependencies to enable Oauth2 Resource server using OKTA OIDC client: Edit
the pom.xml file and add dependencies for Spring Security and Okta. They will enable
the Spring AND Okta OAuth 2.0 for your application.
<!-- security - begin -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>com.okta.spring</groupId>
<artifactId>okta-spring-boot-starter</artifactId>
<version>0.6.1</version>
</dependency>
<!-- security - end -->

ii) Application.yml(or application.properties) config :


You need to modify application.properties as follows (use client_id and client_secret provided
by Okta dashboard to your application):
Note : client_id and client_secret  is not required if you use JWT(Local) token validation.
okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default
okta.oauth2.clientId={clientId}
okta.oauth2.clientSecret={clientSecret}
okta.oauth2.scopes=openid

iii) Java Code Changes :

- Add below annotations to the API application.


@EnableWebSecurity - tells spring we are going to use Spring Security to
provide web security mechanisms

@EnableResourceServer - Annotation that enables request authentication


through OAuth 2.0 tokens. 

- Add jwt() or opaqueToken() to your WebSecurityConfigurerAdapter as shown below.

@Configuration
static class OktaOAuth2WebSecurityConfigurerAdapter extends
WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2ResourceServer().jwt(); //or .opaqueToken();

// process CORS annotations


http.cors();

// force a non-empty response body for 401's to make the


response more browser friendly
Okta.configureResourceServer401ResponseBody(http);
}
}

iv) Test your application:


- Generate an AccessToken using postman or any other tool i.e. https://oidcdebugger.com/debug

- Call your API by passing the Access token in headers as shown below
> export TOKEN=${YOUR_TOKEN}
> curl http://localhost:8080 -H "Authorization: Bearer $TOKEN"
Hello World

3. Using different types of Token Validations:


Based on application requirement, Bearer Access tokens can either choose Local JWT
token validation or Remote token Validation using authorization server’s Token
Introspection/userinfo endpoint.
i) JWT token validation (Local):
In this method, an accesstoken is validated by the resource server by using public
key of authorization server. An resource server can locate public key by using . well-
known endpoint(e.g. https://{yourOktaDomain}//oauth2/default/.well-known/openid-
configuration) to find JWKS url. There is no additional config required if you are using
jwt() in your WebSecurityConfigurerAdapter as shown in code example above.

Note : When using PingFederate as Authorization server


When integrating your API application with PingFederate using JWT access
token validation, you need to follow additional steps

1. Using pingfederate admin console, get the JWKS endpoint from JWT token manager
config as shown below.

Your JWKS endpoint would be https://<pf_host>:<port>/ext/<JWKS Endpoint Path>

e.g. https://cloudsso-test.cisco.com/ext/access/jwks

2. Add this JWKS endpoint to application.yml as shown below 


spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: https://cloudsso-test.cisco.com/ext/access/jwks

ii) Using Token introspection endpoint (Remote):


In this method, resource server makes another call to Oauth
authorization server by passing the access token which needs to validate. If the token is
valid, the authorization server responds if token is active or not and may include
additional attribute in JSON format. Resource server needs to pass it’s ClientID and
Secret in the token validation request.

For token Introspection, you need to replace jwt()with opaqueToken() in your


WebSecurityConfigurerAdapter as shown below.

.oauth2ResourceServer().opaqueToken();

Also add below entry in application.yml file


spring:
security:
oauth2:
resourceserver:
opaque-token:
introspection-uri: https://
{yourOktaDomain}/oauth2/default/v1/introspect
client-id: 0oa143icgzYK0juuS1d7
client-secret: ehb-vScmWRMyyvzRr0ItNNjhhKVHIDDI3FZTrz2R
# Below flag is needed only when you have both userinfo and tokeninfo entries
prefer-token-info: true

You may also need to add below dependency in your pom.xml


<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
</dependency>

iii) Using UserInfo endpoint (Remote):


In this method, resource server makes call to Oauth authorization
server’s userinfo endpoint by passing the access token which needs to validate.
If the token is valid, the authorization server responds with a list of user
attributes (including details such as Group membership details) in JSON format.

To enable userinfo for token validation, you need to add below entry in your
application.yml file
security:
oauth2:
resource:
user-info-uri: https://{yourOktaDomain}/oauth2/default/v1/userinfo
# Below flag is needed only when you have both userinfo and tokeninfo entries
prefer-token-info: false

Extract Username Attribute


Use below code to get logged in username from the access token.
String username =
SecurityContextHolder.getContext().getAuthentication().getPrincipal().getName();

API Access Management


API applications can restrict access to APIs based on certain claims or scope or user’s group
membership.

1. Restrict access to API based on ‘Scope’: Add below line at route level security config in
WebSecurityConfigurerAdapter

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/**").hasAuthority("SCOPE_profile")

2. Restrict access to API based on ‘ClientID’: Extract ClientID from Authentication


object as given below and use it to add conditional access on API

public String getClientID() {


String clientId = null;
Authentication authToken = SecurityContextHolder.getContext().getAuthentication();
Map<String, Object> attributes = new HashMap<>();
if (authToken instanceof OAuth2AuthenticationToken) {
attributes = ((OAuth2AuthenticationToken)
authToken).getPrincipal().getAttributes();
} else if (authToken instanceof JwtAuthenticationToken) {
attributes = ((JwtAuthenticationToken) authToken).getTokenAttributes();
}

// attributes = ((BearerTokenAuthentication) authToken).getTokenAttributes(); //


for token introspection
// clientId = attributes.get("client_id").toString(); // for opaquetoken
clientId = attributes.get("cid").toString(); // for jwt
return clientId;
}

3. Restrict access to API based on Username/Principal attribute :


You can extract username and use as shown below
String username =
SecurityContextHolder.getContext().getAuthentication().getPrincipal().getName();

4. Restrict API access based on User’s group membership : This method works when
userinfo URI is used for token validation (See Section 3 above)

Method 1 : Extract userinfo from UserDetailsService


Spring stored the data returned by UserInfo endpoint into an object of class
UserDetailsService. You need to implement the Interface PrincipalExtractor in order to
extract the userinfo from the response.
Follow steps given below
- Place the attached java file in your project’s main code directory(change attribute name
with actual attribute containing memberOf details.)
private String principalAttribute = "memberOf";

Now the getPrincipal() would return memberOf details.


String principal = authentication.getPrincipal().toString();
// Use it to filter access e.g. below

if (principal.contains("Workforce-okta-admins-testasf")){
return principal;
}else{
return "Access Denied : You are not part of Workforce-okta-admins-test ";
}

Method 2 : Another method is explained at


https://docs.spring.io/spring-security/site/docs/5.2.12.RELEASE/reference/html/webflux-
oauth2.html#webflux-oauth2resourceserver-opaque-userinfo
5. Add Scope/Claim based access on Individual API : You can also add Scope or
claim based access on Individual API level using @PreAuthorize as shown below

@GetMapping("/api/messages")
@PreAuthorize("hasAuthority('SCOPE_profile')")

You might also like