DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Breaking Up a Monolithic Database with Kong
  • User-Friendly API Publishing and Testing With Retrofit
  • Diving Deep Into REST API Channels
  • Building REST API Backend Easily With Ballerina Language

Trending

  • Event-Driven Architectures: Designing Scalable and Resilient Cloud Solutions
  • A Developer's Guide to Mastering Agentic AI: From Theory to Practice
  • Unlocking AI Coding Assistants Part 4: Generate Spring Boot Application
  • Breaking Bottlenecks: Applying the Theory of Constraints to Software Development
  1. DZone
  2. Data Engineering
  3. Databases
  4. Building a Secure REST API with OpenID Connect

Building a Secure REST API with OpenID Connect

In this article, we’ll take a look at building a secured REST API by integrating with Okta as the identity provider via OpenID Connect (OIDC).

By 
Anjana Fernando user avatar
Anjana Fernando
·
Jul. 14, 20 · Tutorial
Likes (5)
Comment
Save
Tweet
Share
16.1K Views

Join the DZone community and get the full member experience.

Join For Free

Introduction

In this article, we’ll take a look at building a secured REST API by integrating with Okta as the identity provider via OpenID Connect (OIDC). This article is based on the DZone article Building a Java REST API with Quarkus, which explains how to create a Java REST API with Quarkus and Okta. We will be implementing a similar scenario here by using Ballerinalang, and show how it’s simpler and more straightforward to implement compared to our Java counterpart. 

Prerequisites

  • Ballerina Installation(>= v1.2.6)
    • Verify the installation by typing “ballerina -v” in the command line. This should output the currently installed Ballerina version. 

  • Okta Developer Account: An Okta developer account can be created by navigating to https://developer.okta.com/. 
  • CURL or another suitable HTTP client for your respective environment.

Hello World Ballerina Service

Let’s start off by creating a simple hello world service application as our base scenario. Add the following code to a file named hello.bal.

hello world

Listing 1: Hello World Service

The above service can be run by using the following command:

Shell
 




x


 
1
$ ballerina run hello.bal
2

          
3
[ballerina/http] started HTTP/WS listener 0.0.0.0:8080



The final source code of our hello world service can be found here. 

Let’s invoke the service by sending a request. 

Shell
 




xxxxxxxxxx
1


 
1
$ curl http://localhost:8080/secured/hello
2
Hello Anonymous, authScheme: N/A



Here, the service is invoked through HTTP without any form of user authentication. 

A Secured Greeting

Let’s update our hello world service in order to authenticate users who invoke it using a JWT.

JWT

Listing 2: Secured Hello World Service with JWT

We have done several new things to our earlier service implementation. First, our HTTP listener that was bound to the service construct HelloService has been changed from new http:Listener(8080) to httpsListener. Earlier we created an anonymous HTTP listener object in place, and now, we have created a separate HTTPS listener object and referred to it from the service. This approach is required when we want to provide additional configurations to the listener compared to the inline creation. A Ballerina service is not limited to having only one listener, but it can have multiple compatible listeners attached to a single service at a time. The listener object simply needs to be given as a comma-separated list.

HTTP listener objects provide the functionality to associate a set of authentication and authorization providers. In our case, we have created a single JWT inbound auth provider and registered as an auth handler in the HTTPS listener configuration. In Ballerina, it is mandated that a secure transport should be used in an authentication scenario such as in our case. This is where a bearer token is sent through the headers and would be susceptible to a man-in-the-middle attack if a secured protocol is not used. 

Figure 1 shows a summary of the relationship between the Ballerina services, listeners, and authentication providers. 

ballerina-service

Figure 1: Ballerina Service-Listener-Authentication Provider Relationship

Let’s try invoking the updated service above. 

Shell
 




xxxxxxxxxx
1


 
1
$ curl -k https://localhost:8443/secured/hello
2
Authentication failure.



As we see above, the invocation to the updated service resource fails with an authentication failure. The service returns an HTTP 401 Unauthorized message. This can be checked by passing in the -v switch to the CURL command above. 

The truststore.p12 and the keystore.p12 files are keystore files used in the HTTPS communication. For the purpose of this demo, I’ve copied them from Ballerina default keystore files, which can be found at ${BALLERINA_HOME}/bre/security/. ${BALLERINA_HOME} can be found by executing ballerina home in the command line. For JWT signature verification, the public keys are provided using the JSON Web Key Set (JWKS) format. This is shown at line 9 in Listing 2. 

In order to access the service, we have to send a JWT with the service request to authenticate the user. Let’s see how this can be generated using OIDC with Okta. 

OIDC Application Integration With Okta

In this section, we will use our Okta developer account to create a new OIDC application, and then generate a JWT in order to invoke our secure service. These are the steps you need to follow:

  • Navigate to your domain by clicking on the top-right menu and selecting Your Org
  • Click on Applications and then Add Application
  • Select the application type Web 
  • Provide a name, e.g., Ballerina Demo
  • Update the Login redirect URIs with “https://oidcdebugger.com/debug”
  • Under Grant type allowed set Implicit (Hybrid)
  • Leave the rest with the default values
  • Click Done
  • Note down the Client ID

We will also add some custom scopes to our authorization server in order to use them in our services. 

  • Click the top menu API -> Authorization Servers
  • Select default and click Scopes
  • Add the new scopes greet, products_access, products_add, and products_delete

The updated scopes will look similar to Figure 2. 

okta

Figure 2: Okta Authorization Server Scopes

Now, we can execute an OIDC request in order to create an access token to authenticate users for our service. Let’s navigate to https://oidcdebugger.com/. 

implicit flow

Figure 3: OIDC Request Generation with https://oidcdebugger.com/

As shown in Figure 3, fill in the <your-okta-domain> and <your-client-id> fields with your respective values. Note that we have provided the scopes openid, email, profile, and greet, which is required for our service resource. After the fields are filled, click Send Request and you will be presented with a screen similar to Figure 4. 

successful flow

Figure 4: OIDC Response with the JWT Access Token

Copy the access token that is generated, and set it as a shell variable. 

Shell
 




xxxxxxxxxx
1


 
1
$ TOKEN=eyJraWQiOiI3...



Now that we have a JWT token to authenticate the user from Okta, we will be able to use this with our service to do the authentication.

Shell
 




xxxxxxxxxx
1


 
1
$ curl -k -H "Authorization: Bearer $TOKEN" https://localhost:8443/secured/hello
2
Hello [email protected], authScheme: jwt groups: Everyone



As we can see above, now the service invocation succeeds, since we provided a valid JWT token. Also, in our service implementation, we have stated that we require the greet scope in order to invoke the hello resource in the service.  We can create another access token using https://oidcdebugger.com/ without this scope and see that we cannot invoke the service resource anymore. 

Data Service With Access Control

Now that we know the generation functionality of using OIDC in creating the JWT for our services, let’s create another general scenario for using access control in our service. We will be creating a simple data service that represents a product catalog, where we will control the reading and writing operations of it based on the authenticated user’s privileges. For this, we will be using the scopes products_add, products_access, and products_delete. 

Listing 3 shows the implementation of this data service.

product catalogue

Listing 3: Product Catalog Data Service

Here, we can see we have simply mapped the service resources with the service paths and respective HTTP methods in order to define the functionality. Each resource requires a specific scope in order to invoke the resource. In this manner, we can have a fine-grained access control mechanism when defining the operations in our system. 

The service above can be tested by creating tokens with different scope combinations to verify the access control features. 

Sample Run

The full source code of our product catalog data service can be found here. 

Shell
 




xxxxxxxxxx
1
27


 
1
$ ballerina run product-catalog-service.bal
2
[ballerina/http] started HTTPS/WSS listener 0.0.0.0:8443
3

          
4
$ curl -d '{"id":"id1","name":"Pixel 4 XL","price":899.0}' -k -H "Authorization: Bearer $TK_GREET" https://localhost:8443/ProductCatalog/product
5
Authorization failure.
6

          
7
$ curl -d '{"id":"id1","name":"Pixel 4 XL","price":899.0}' -k -H "Authorization: Bearer $TK_PRODUCT_ACCESS" https://localhost:8443/ProductCatalog/product
8
Authentication failure.
9

          
10
$ curl -d '{"id":"id1","name":"Pixel 4 XL","price":899.0}' -k -H "Authorization: Bearer $TK_PRODUCTS_ADD" https://localhost:8443/ProductCatalog/product
11

          
12
$ curl -d '{"id":"id2","name":"Pixel 3","price":599.0}' -k -H "Authorization: Bearer $TK_PRODUCTS_ADD" https://localhost:8443/ProductCatalog/product
13

          
14
$ curl -k -H "Authorization: Bearer $TK_GREET" https://localhost:8443/ProductCatalog/product
15
Authorization failure.
16

          
17
$ curl -k -H "Authorization: Bearer $TK_PRODUCTS_ACCESS" https://localhost:8443/ProductCatalog/product
18
[{"id":"id1", "name":"Pixel 4 XL", "price":899}, {"id":"id2", "name":"Pixel 3", "price":599}]
19

          
20
$ curl -X DELETE -k -H "Authorization: Bearer $TK_PRODUCTS_ACCESS" https://localhost:8443/ProductCatalog/product/id1
21
Authorization failure.
22

          
23
$ curl -X DELETE -k -H "Authorization: Bearer $TK_PRODUCTS_DELETE" https://localhost:8443/ProductCatalog/product/id1
24

          
25
$ curl -k -H "Authorization: Bearer $TK_PRODUCTS_ACCESS" https://localhost:8443/ProductCatalog/product
26
[{"id":"id2", "name":"Pixel 3", "price":599}]



Summary

In this article, we have looked into how we can secure a REST service written in Ballerina using Okta as the identity provider. We have used OIDC when generating a JWT access token to access our service resources. 

For more information on writing microservices in Ballerina, check out the following resources: 

  • Ballerina by Example
  • Ballerina API Documentation
API REST Web Protocols Web Service microservice OpenID

Opinions expressed by DZone contributors are their own.

Related

  • Breaking Up a Monolithic Database with Kong
  • User-Friendly API Publishing and Testing With Retrofit
  • Diving Deep Into REST API Channels
  • Building REST API Backend Easily With Ballerina Language

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • [email protected]

Let's be friends: