In this article, we’ll take a look at building a secured REST API by integrating with as the identity provider via OpenID Connect (OIDC). This article is based on the DZone article , which explains how to create a Java REST API with Quarkus and Okta. We will be implementing a similar scenario here by using , and show how it’s simpler and more straightforward to implement compared to our Java counterpart. Okta Building a Java REST API with Quarkus Ballerinalang Prerequisites (>= v1.2.6) Ballerina Installation Verify the installation by typing in the command line. This should output the currently installed Ballerina version. ballerina -v 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. Listing 1: Hello World Service The above service can be run by using the following command: $ ballerina run hello.bal [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. $ curl http://localhost:8080/secured/hello 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. 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 has been changed from to . 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. HelloService new http:Listener(8080) httpsListener 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. Figure 1: Ballerina Service-Listener-Authentication Provider Relationship Let’s try invoking the updated service above. $ curl -k https://localhost:8443/secured/hello 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 and the 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 . 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. truststore.p12 keystore.p12 ${BALLERINA_HOME}/bre/security/ ${BALLERINA_HOME} 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 and then Applications Add Application Select the application type Web Provide a name, e.g., Ballerina Demo Update the with “ ” Login redirect URIs https://oidcdebugger.com/debug Under set Grant type allowed 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 and click default Scopes Add the new scopes , , , and greet products_access products_add products_delete The updated scopes will look similar to Figure 2. 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/ Figure 3: OIDC Request Generation with https://oidcdebugger.com/ As shown in Figure 3, fill in the and fields with your respective values. Note that we have provided the scopes , , , and , which is required for our service resource. After the fields are filled, click and you will be presented with a screen similar to Figure 4. <your-okta-domain> <your-client-id> openid email profile greet Send Request Figure 4: OIDC Response with the JWT Access Token Copy the access token that is generated, and set it as a shell variable. $ 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. $ curl -k -H https://localhost:8443/secured/hello Hello lafernando@gmail.com, authScheme: jwt groups: Everyone "Authorization: Bearer " $TOKEN 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 scope in order to invoke the resource in the service. We can create another access token using without this scope and see that we cannot invoke the service resource anymore. greet hello https://oidcdebugger.com/ 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 , , and . products_add products_access products_delete Listing 3 shows the implementation of this data service. 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 $ ballerina run product-catalog-service.bal [ballerina/http] started HTTPS/WSS listener 0.0.0.0:8443 $ curl -d -k -H https://localhost:8443/ProductCatalog/product Authorization failure. $ curl -d -k -H https://localhost:8443/ProductCatalog/product Authentication failure. $ curl -d -k -H https://localhost:8443/ProductCatalog/product $ curl -d -k -H https://localhost:8443/ProductCatalog/product $ curl -k -H https://localhost:8443/ProductCatalog/product Authorization failure. $ curl -k -H https://localhost:8443/ProductCatalog/product [{ : , : , :899}, { : , : , :599}] $ curl -X DELETE -k -H https://localhost:8443/ProductCatalog/product/id1 Authorization failure. $ curl -X DELETE -k -H https://localhost:8443/ProductCatalog/product/id1 $ curl -k -H https://localhost:8443/ProductCatalog/product [{ : , : , :599}] '{"id":"id1","name":"Pixel 4 XL","price":899.0}' "Authorization: Bearer " $TK_GREET '{"id":"id1","name":"Pixel 4 XL","price":899.0}' "Authorization: Bearer " $TK_PRODUCT_ACCESS '{"id":"id1","name":"Pixel 4 XL","price":899.0}' "Authorization: Bearer " $TK_PRODUCTS_ADD '{"id":"id2","name":"Pixel 3","price":599.0}' "Authorization: Bearer " $TK_PRODUCTS_ADD "Authorization: Bearer " $TK_GREET "Authorization: Bearer " $TK_PRODUCTS_ACCESS "id" "id1" "name" "Pixel 4 XL" "price" "id" "id2" "name" "Pixel 3" "price" "Authorization: Bearer " $TK_PRODUCTS_ACCESS "Authorization: Bearer " $TK_PRODUCTS_DELETE "Authorization: Bearer " $TK_PRODUCTS_ACCESS "id" "id2" "name" "Pixel 3" "price" 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