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
Please enter at least three characters to search
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

Because the DevOps movement has redefined engineering responsibilities, SREs now have to become stewards of observability strategy.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Related

  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Authentication With Remote LDAP Server in Spring Web MVC
  • The First Annual Recap From JPA Buddy
  • How To Build Web Service Using Spring Boot 2.x

Trending

  • Rust, WASM, and Edge: Next-Level Performance
  • Enforcing Architecture With ArchUnit in Java
  • Monolith: The Good, The Bad and The Ugly
  • Event-Driven Microservices: How Kafka and RabbitMQ Power Scalable Systems
  1. DZone
  2. Coding
  3. Java
  4. How to Use Kotlin to Create a Secure Ktor Application

How to Use Kotlin to Create a Secure Ktor Application

In this article, see how to use a modern JVM stack to build your own Nano Blogging Service, or nabl, using Ktor.

By 
Ruslan Zaharov user avatar
Ruslan Zaharov
·
Nov. 25, 20 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
13.6K Views

Join the DZone community and get the full member experience.

Join For Free

Today I will be showing you how to use a modern JVM stack to build your own Nano Blogging Service, or nabl. You will be using the Ktor web framework, Kotlin programming language, and securing it with Okta. Your users will be able to sign up/login, as well as browse chronological feeds and post updates without the inclusion of ads.

Often Kotlin is described as a better alternative to Java due to its efficient Java interoperability. This is important as it then allows for you to employ the largest ecosystem of existing libraries written and designed for Java, as well as JVM frameworks in your Kotlin application, or vice-versa. Kotlin also is compatible with Spring Boot, Jersey, Dropwizard, and more. Any framework that is “Kotlin-native” provides high-level language support, additional type-safety, and other competitive advantages. 

The most prominent “Kotlin-native” web framework officially supported by JetBrains is Ktor, which allows for a customizable modular framework, giving developers full control over the execution of the applications, all while providing practical defaults. 

Prerequisites

  • Computer with downloaded Java 8+, Git, bash-like command line
  • Familiarity with Java or Kotlin
  • Your favorite IDE, for instance, IntelliJ IDEA Community Edition
  • A Free Okta Developer account
  • 15 mins of your time

Build a Ktor Application With Kotlin

As with any web application framework, Ktor provides several libraries and imposes some conventions. Don’t worry—it doesn’t tell you how to write your code. The conventions are mostly for the HTTP layer and you’re free to write other lower layers the way you want. A few of the most notable things include:

  • The web application is a pipeline processing incoming requests through features and route handlers.
  • Request handling is non-blocking; it relies on Kotlin coroutines.
  • The configuration file format is HOCON.
  • Framework is employing DSL for the top-level declarations, e.g., modules setup, routing, etc.
  • Pluggable features are configured using install(FeatureObject) { config }.
  • Most of the functions and properties you use are extension functions.

Ktor Project Structure

The application in this example depends on several libraries:

  • Kotlin programming language you use for this project
  • Ktor server with Ktor server CIO - server implementation and coroutine-based HTTP engine core
  • Ktor client with Ktor client CIO - client used to communicate to OAuth2 server
  • Ktor Auth module to handle authorization flow
  • kotlinx.html set of classes allowing to write type-safe HTML generators
  • Okta JWT Verifier library helps to parse and verify access and id tokens

You can bootstrap this tutorial by cloning our Git repository and starting with the initial branch:

Java
 




x


 
1
git clone -b initial https://github.com/oktadeveloper/okta-kotlin-ktor-example.git
2
cd okta-kotlin-ktor-example


NOTE: If you want to see the completed app, with Okta already integrated, you can check out the main branch. See the project’s README for instructions on how to configure it to work with your Okta account.

Start your Ktor Application

Use IntelliJ runner or type ./gradlew run in the command line to start your application, point your web browser to http://localhost:8080.

All the messages displayed are from the in-memory database. Note that at this stage, the user can’t log in; hence they can’t post messages.

Secure Your Ktor Application With Okta

Real-world applications often require users to log in to perform some actions or access information. User management and security are much more complicated than they might seem and it can be tough to make them right. If you have done it previously, you know what I’m talking about.

User management shouldn’t take much of your time because that problem is solved already, right? In this tutorial, you’ll be using Okta’s OAuth 2.0 authorization service along with OpenID Connect (OIDC). Okta provides many features for both enterprise and personal project needs - MFA, SAML, groups, policies, social media logins, and many more. We offer solutions for different size companies - from pet projects just for yourself to big enterprises such as FedEx, Box, HubSpot, Experian, and many others. Okta helps developers implement secure authentication, handles authorization, and can act as an identity provider with a minimum effort and lines of code.

If you haven’t created an Okta account yet, sign up first. It’s free, no credit card required.

Login to the Okta admin console. On the top menu select Applications → Add Application:

Then, configure your Okta application. Don’t worry, if you want to change anything it’s always possible to return to this screen. At the very least, you need to set the following settings:

  • Name - give it a meaningful name, for instance, My Ktor nano Blogging Service
  • Base URIs - put http://localhost:8080/ there. Multiple URI can be provided; you can add more URIs if needed.
  • Login redirect URIs - set it to http://localhost:8080/login/authorization-callback. Upon successful login, the user will be redirected to URI provided with tokens in the query.
  • Logout redirect URIs - value http://localhost:8080 allows you to provide a redirect URL on successful logout.

Click Done to finish the initial setup.

Take note of the following three values. You’ll use them in your Ktor application:

  • Org URL: Hover over API on the top menu bar, and select Authorization Servers menu item, copy the value from Issuer URI

  • Client ID and Client Secret as below:

Configure Ktor’s OAuth 2.0 Module

Ktor has an implementation of OAuth Client—it just needs to be configured. It’s always good practice to never insert any keys, tokens, or credentials directly into the code. Even for a demo project. To inject Okta parameters from environment variables, append a new block in resources/application.conf:

Java
 




xxxxxxxxxx
1


 
1
...
2
okta {
3
    orgUrl = ${OKTA_ORGURL}
4
    clientId = ${OKTA_CLIENT_ID}
5
    clientSecret = ${OKTA_CLIENT_SECRET}
6
}


To start your application from IntelliJ IDEA or any other IDE, these environment variables must be provided. In the Run/Debug Configuration dialog, click on the Environment variables and specify them as I have below.

Then, create a src/auth-settings.kt file to contain all Okta-configuration related functions.

You could also create an okta.env file with the following code:

Java
 




xxxxxxxxxx
1


 
1
export OKTA_ORGURL=https://{yourOktaDomain}/oauth2/default
2
export OKTA_CLIENT_ID={yourClientId}
3
export OKTA_CLIENT_SECRET={yourClientSecret}


Next, run source okta.env before running your app.

If you’re on Windows, name the file okta.bat and use SET instead of export.

Add a generic configuration class for Okta services in src/auth-settings.kt.

Java
 




xxxxxxxxxx
1
10


 
1
data class OktaConfig(
2
    val orgUrl: String,
3
    val clientId: String,
4
    val clientSecret: String,
5
    val audience: String
6
) {
7
    val accessTokenUrl = "$orgUrl/v1/token"
8
    val authorizeUrl = "$orgUrl/v1/authorize"
9
    val logoutUrl = "$orgUrl/v1/logout"
10
}


Create a configuration reader in src/auth-settings.kt. This takes a Config object, reads from it, and creates an OktaConfig object.

Java
 




xxxxxxxxxx
1


 
1
fun oktaConfigReader(config: Config): OktaConfig = OktaConfig(
2
    orgUrl = config.getString("okta.orgUrl"),
3
    clientId = config.getString("okta.clientId"),
4
    clientSecret = config.getString("okta.clientSecret"),
5
    audience = config.tryGetString("okta.audience") ?: "api://default"
6
)


Finally, the Ktor Auth module is expecting configuration to be passed as OAuthServerSettings.OAuth2ServerSettings. For that, you need a mapping function in src/auth-settings.kt:

Java
 




xxxxxxxxxx
1
10


 
1
fun OktaConfig.asOAuth2Config(): OAuthServerSettings.OAuth2ServerSettings =
2
    OAuthServerSettings.OAuth2ServerSettings(
3
        name = "okta",
4
        authorizeUrl = authorizeUrl,
5
        accessTokenUrl = accessTokenUrl,
6
        clientId = clientId,
7
        clientSecret = clientSecret,
8
        defaultScopes = listOf("openid", "profile"),
9
        requestMethod = Post
10
    )


Setup a Ktor Authentication Module

All authentication configuration and handling happen inside the setupAuth() function of src/auth.kt file. Start filling it with configuration. Use oktaConfigReader() to read configuration from the application file. Then, install the Authentication feature and configure it to use OAuth, provide it a redirect callback, the Okta OAuth2 configuration, and a default HttpClient for the Ktor OAuth client features.

Java
 




xxxxxxxxxx
1
19


 
1
package com.okta.demo.ktor
2
 
3
import com.typesafe.config.ConfigFactory
4
import com.okta.jwt.JwtVerifiers
5
import io.ktor.application.*
6
import io.ktor.auth.*
7
import io.ktor.client.*
8
 
9
fun Application.setupAuth() {
10
    val oktaConfig = oktaConfigReader(ConfigFactory.load() ?: throw Exception("Could not load config"))
11
    install(Authentication) {
12
        oauth {
13
            urlProvider = { "http://localhost:8080/login/authorization-callback" }
14
            providerLookup = { oktaConfig.asOAuth2Config() }
15
            client = HttpClient()
16
        }
17
    }
18
}


To ensure that tokens provided are valid, they need to be verified. This can be done using theOkta JWT Verifier library. Construct access token and ID token verifiers as follows:


Java
 




xxxxxxxxxx
1


 
1
val accessTokenVerifier = JwtVerifiers.accessTokenVerifierBuilder()
2
    .setAudience(oktaConfig.audience)
3
    .setIssuer(oktaConfig.orgUrl)
4
    .build()
5
val idVerifier = JwtVerifiers.idTokenVerifierBuilder()
6
    .setClientId(oktaConfig.clientId)
7
    .setIssuer(oktaConfig.orgUrl)
8
    .build()


Next, configure three login-specific endpoints. Ktor DSL assumes the following structure:


Java
 




xxxxxxxxxx
1
19


 
1
fun Application.setupAuth() {
2
    ...
3
    routing {
4
        authenticate {
5
            // Okta calls this endpoint providing accessToken along with requested idToken
6
            get("/login/authorization-callback") {
7
                // ⚫ handle authorization
8
            }
9
            // When guest accessing /login it automatically redirects to okta login page
10
            get("/login") {
11
                // ⚫ perform login
12
            }
13
        }
14
        // Perform logout by cleaning cookies
15
        get("/logout") {
16
            // ⚫ perform logout
17
        }
18
    }
19
}


Sign in with the /login endpoint

It’s the easiest one. Ktor will require user authentication for all endpoints located within the authenticate block. If a user is not authenticated, they will be redirected to the authorization URL. Its value is taken from the authorizeUrl property from OktaConfig.

Since the Ktor Auth module is handling this itself, the implementation is a single line. The condition checks if a visitor has a session and, if so, redirects it to the root of the website:

Java
 




xxxxxxxxxx
1


 
1
// When guest accessing /login it automatically redirects to okta login page
2
get("/login") {
3
    call.respondRedirect("/")
4
}


Authorization endpoint /login/authorization-callback

Upon successful authorization, the user is redirected to this URL. The Okta authorization service provides access and ID tokens as part of the login flow. If unsure, read our Illustrated Guide to OAuth and OIDC.

To extract information (aka, parse the JWT) about the user, you can use Okta’s JWT Verifier. In the code below, the user’s name is taken from the token’s claims and “slugified”, to create a URL-safe alphanumeric username. Finally, a new session is created and the user redirected to the /.


Java
 




xxxxxxxxxx
1
22


 
1
// Okta calls this endpoint providing accessToken along with requested idToken
2
get("/login/authorization-callback") {
3
    // Get a principal from from OAuth2 token
4
    val principal = call.authentication.principal<OAuthAccessTokenResponse.OAuth2>()
5
        ?: throw Exception("No principal was given")
6
    // Parse and verify access token with OktaJwtVerifier
7
    val accessToken = accessTokenVerifier.decode(principal.accessToken)
8
    // Get idTokenString, parse and verify id token
9
    val idTokenString = principal.extraParameters["id_token"]
10
        ?: throw Exception("id_token wasn't returned")
11
    val idToken = idVerifier.decode(idTokenString, null)
12
    // Try to get handle from the id token, of failback to subject field in access token
13
    val fullName = (idToken.claims["name"] ?: accessToken.claims["sub"] ?: "UNKNOWN_NAME").toString()
14
    println("User $fullName logged in successfully")
15
    // Create a session object with "slugified" username
16
    val session = UserSession(
17
        username = fullName.replace("[^a-zA-Z0-9]".toRegex(), ""),
18
        idToken = idTokenString
19
    )
20
    call.sessions.set(session)
21
    call.respondRedirect("/")
22
}


Logout endpoint /logout

Users might have reasons to log out from the website—they might even simply erase cookies! Some people may consider that a little bit too technical. You can help them to do so by resetting the session on the server-side:

Java
 




xxxxxxxxxx
1


 
1
// Perform logout by cleaning session
2
get("/logout") {
3
    call.sessions.clear<UserSession>()
4
    call.respondRedirect("/")
5
}


Start Your Kotlin + Ktor Application

Run your application, open your browser to http://localhost:8080, and click Login from the top menu bar. You will see an Okta login screen. After you type your credentials you’ll be redirected back to the app but as a user this time. Try to send some messages!

Congratulations, you just added authorization to your service!

Logout with Okta

Did you try to t̶u̶r̶n̶ ̶i̶t̶ ̶o̶f̶f̶,̶ ̶t̶h̶e̶n̶ ̶o̶n̶ ̶a̶g̶a̶i̶n̶ logout and login again? You might observe an unexpected behavior. If you checked “remember me” box in the Okta screen, you virtually can’t log out—or at least it looks like that.

From the user’s point of view, they expect to see a login screen inviting to put login/password——not to automatically be logged in:

You might ask yourself: why is it done this way? Why doesn’t the Authorization server purge sessions?

What if you’re using Facebook instead of Okta as an Authorization and Identity Provider service? And you want to logout from some website and that website also destroys your session in Facebook. It doesn’t sound nice, does it?

If you intend to logout users from Okta, as well, you’ll need to use something called RP-Initiated Logout. You can read more about it in this blog post. The basic idea is straightforward - after you remove a session inside your app, the user needs to visit a specially formed logoutUrl with idToken provided as a GET parameter. Update your logout handler in src/auth.kt:

Java
 




xxxxxxxxxx
1
17


 
1
// Perform logout by cleaning cookies and start RP-initiated logout
2
get("/logout") {
3
    val idToken = call.session?.idToken
4

          
5
    call.sessions.clear<UserSession>()
6

          
7
    val redirectLogout = when (idToken) {
8
        null -> "/"
9
        else -> URLBuilder(oktaConfig.logoutUrl).run {
10
            parameters.append("post_logout_redirect_uri", "http://localhost:8080")
11
            parameters.append("id_token_hint", idToken)
12
            buildString()
13
        }
14
    }
15

          
16
    call.respondRedirect(redirectLogout)
17
}


Restart your application and try to logout. Now the application behaves as you’d expect:

Manage Users With Okta

The Nano Blogging Service is more fun when different people can log in! You can create additional users from the Okta Developer Console. From the top menu bar, click on Users, then Add Person. You’ll be presented with a dialog to add a new user:

Enable User Registration

Okta also provides a self-sign up service. You can enable it by heading to the Okta Developer Console, hovering over the Users top menu item, and selecting Registration from the sub-menu. Okta will show a single button you need to click to activate the feature:

If desired, tune the default options and save.

Then, when you try to sign in to your service, you’ll see a “Sign up” link:

Ktor Secure App Code Review

Now that you have everything working, let’s take a look at the Kotlin code that makes it all possible.

Ktor Data Layer

Look at the basic data models of your application in the src/entities.kt file:

Java
 




xxxxxxxxxx
1
14


 
1
package com.okta.demo.ktor
2
 
3
import java.time.LocalDateTime
4
 
5
data class BlogRecord(
6
    val userHandle: String,
7
    val text: String,
8
    val createdAt: LocalDateTime = LocalDateTime.now()
9
)
10
 
11
data class UserSession(
12
    val username: String,
13
    val idToken: String
14
)


The BlogRecord class contains information about the userHandle, posted text and createdAt timestamp. UserSession is an object which contains information about a currently signed in user; see the authentication section for more details.

The BlogRecordRepository class is responsible for data manipulation. For demo purposes, data is stored in memory and initialized with some dummy records at startup time.

Your data repository is in the src/BlogRecordRepository.kt file:

Java
 




xxxxxxxxxx
1
24


 
1
package com.okta.demo.ktor
2
 
3
class BlogRecordRepository {
4
    private val records = mutableListOf<BlogRecord>()
5
 
6
    val all: List<BlogRecord>
7
        get() = records
8
 
9
    fun insert(userHandle: String, text: String) {
10
        records += BlogRecord(userHandle, text)
11
    }
12
 
13
    fun byUser(userHandle: String) = records.filter { it.userHandle == userHandle }
14
}
15
 
16
val blogRecords = BlogRecordRepository().apply {
17
    insert("kack", "Hello world!")
18
    insert("kack", "Keep messages short and sweet!")
19
    insert("ann", "OMG it's a future unikorn!")
20
    insert("rux", "Chronological feed! It's just like the good old days! ")
21
    insert("kotlin", "Wise language selection")
22
    insert("whitestone", "We'd like to invest")
23
    insert("cat", "")
24
}


Ktor Main Application Configuration

Before you get into the route handling and login flow, the web service itself needs to be configured. As per convention, Ktor services are configured by creating an Application.module() extension function. Look at the configuration sections in src/application.kt:

Java
 




xxxxxxxxxx
1
45


 
1
package com.okta.demo.ktor
2
 
3
import io.ktor.application.*
4
import io.ktor.features.*
5
import io.ktor.request.*
6
import io.ktor.sessions.*
7
import io.ktor.util.*
8
import org.slf4j.event.Level
9
import kotlin.collections.set
10
 
11
fun main(args: Array<String>): Unit = io.ktor.server.cio.EngineMain.main(args)
12
 
13
@Suppress("unused") // Referenced in application.conf
14
@kotlin.jvm.JvmOverloads
15
fun Application.module(testing: Boolean = false) {
16
    // Sessions are stored in encrypted cookies
17
    install(Sessions) {
18
        cookie<UserSession>("MY_SESSION") {
19
            val secretEncryptKey = hex("00112233445566778899aabbccddeeff")
20
            val secretAuthKey = hex("02030405060708090a0b0c")
21
            cookie.extensions["SameSite"] = "lax"
22
            cookie.httpOnly = true
23
            transform(SessionTransportTransformerEncrypt(secretEncryptKey, secretAuthKey))
24
        }
25
    }
26

          
27
    // Respond for HEAD verb
28
    install(AutoHeadResponse)
29

          
30
    // Load each request
31
    install(CallLogging) {
32
        level = Level.INFO
33
        filter { call -> call.request.path().startsWith("/") }
34
    }
35

          
36
    // Configure ktor to use OAuth and register relevant routes
37
    setupAuth()
38

          
39
    // Register application routes
40
    setupRoutes()
41
}
42
 
43
// Shortcut for the current session
44
val ApplicationCall.session: UserSession?
45
    get() = sessions.get<UserSession>()


Your application module configures the session handler to keep data in encrypted cookies and enable logging, which is very useful for debugging. Two of the functions - setupAuth() and setupRoutes()- configure OAuth 2.0 and setup web service routes.

Ktor Service Routes

This application registers two routes with Ktor DSL making it very expressive:

  • POST / takes a text parameter from the body and current actor(user handle) from the session and creates a new nano blog record. Both actor and text must be valid to create a new record; otherwise, an error is thrown. Upon a successful insertion, the user gets redirected to the /.
  • GET /{username?} effectively handles all GET requests and attempts to extract the username URL parameter if present. Then, it renders the main template with either global or requested user’s feed using the feedPage() method.

See src/routes.kt:


Java
 




xxxxxxxxxx
1
31


 
1
package com.okta.demo.ktor
2
 
3
import io.ktor.application.*
4
import io.ktor.html.*
5
import io.ktor.request.*
6
import io.ktor.response.*
7
import io.ktor.routing.*
8
 
9
fun Application.setupRoutes() = routing {
10
    post("/") { root ->
11
        val actor = call.session?.username
12
            ?: throw Exception("User must be logged in first")
13
        val text = call.receiveParameters()["text"]?.takeIf(String::isNotBlank)
14
            ?: throw Exception("Invalid request - text must be provided")
15

          
16
        blogRecords.insert(actor, text)
17

          
18
        call.respondRedirect("/")
19
    }
20

          
21
    get("/{username?}") {
22
        val username = call.parameters["username"]
23
        call.respondHtmlTemplate(MainTemplate(call.session?.username)) {
24
            content {
25
                val canSendMessage = call.session != null
26
                if (username == null) feedPage(" Home feed", blogRecords.all, canSendMessage)
27
                else feedPage(" ${username}'s blog", blogRecords.byUser(username), canSendMessage)
28
            }
29
        }
30
    }
31
}


The page-rendering function, feedPage(), takes three parameters: page title, list of the nano blog posts to render, and a boolean flag canSendMessage (if it’s true, the text submission form will be visible). The variable canSendMessage is set to true only when the current user has an active session, that is possible only after login.

Type-Safe Views With Kotlin

Kotlin syntax empowers developers to create type-safe DSL. This Nano Blogging Service is using the kotlinx.html library, which provides HTML-like syntax for HTML-rendering. All the views are in the src/views.kt file.

The primary and only template MainTemplate includes Bootstrap CSS library, renders the top navbar menu, and provides a basic layout for the frontend:

Java
 




x


 
1
/**
2
* Generic web page template, contains content placeholder where
3
* content should be placed
4
*/
5
class MainTemplate(private val currentUsername: String? = null) : Template<HTML> {
6
    val content = Placeholder<HtmlBlockTag>()
7
    override fun HTML.apply() {
8
        head {
9
            title { +"Nano Blogging Service" }
10
            styleLink("https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css")
11
            meta(name = "viewport", content = "width=device-width, initial-scale=1, shrink-to-fit=no")
12
            meta(charset = "utf-8")
13
        }
14
        body("d-flex flex-column h-100") {
15
            header {
16
                div("navbar navbar-dark bg-dark shadow-sm") {
17
                    div("container") {
18
                        a(href = "/", classes = "font-weight-bold navbar-brand") {
19
                            +" NANO BLOGGING SERVICE"
20
                        }
21
                        div("navbar-nav flex-row") {
22
                            if (currentUsername != null) {
23
                                a(href = "/${currentUsername}", classes = "nav-link mr-4") {
24
                                    +"Hello, $currentUsername"
25
                                }
26
                                a(href = "/logout", classes = "nav-link") {
27
                                    +"Logout"
28
                                }
29
                            } else {
30
                                div("navbar-text mr-4") {
31
                                    +"Hello, Guest"
32
                                }
33
                                div("navbar-item") {
34
                                    a(href = "/login", classes = "nav-link") {
35
                                        +"Login"
36
                                    }
37
                                }
38
                            }
39
                        }
40
                    }
41
                }
42
            }
43
            main("flex-shrink-0 mt-3") {
44
                div("container col-xs-12 col-lg-8") {
45
                    insert(content)
46
                }
47
            }
48
        }
49
    }
50
}


Confused about plus(+) sign in front of the string inside HTML elements? Don’t worry. It’s just a shortcut for the text() function which sets current tag content.

View blocks such as feedBlock(), sendMessageForm() and feedPage() are extension functions (I know, there’s a lot of them!) on FlowContent. That prevents global scope pollution with enormous HTML DSL elements and provides better encapsulation.

Learn More About Ktor and Kotlin

Congratulations on finishing this tutorial! You built a Kotlin and Ktor-based Nano Blogging Service secured with Auth 2.0.

The source code for this tutorial is available on GitHub in the oktadeveloper/okta-kotlin-ktor-example repository.

If you liked this post, you might like these others too:

  • What the Heck is OAuth?
  • A Quick Guide to OAuth 2.0 with Spring Security
  • Deploy a Secure Spring Boot App to Heroku

Make sure to follow us on Twitter and subscribe to our YouTube Channel so that you never miss any of our developer content!

application Spring Security Kotlin (programming language) authentication Java (programming language) Database Web Service intellij

Published at DZone with permission of Ruslan Zaharov. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Enterprise RIA With Spring 3, Flex 4 and GraniteDS
  • Authentication With Remote LDAP Server in Spring Web MVC
  • The First Annual Recap From JPA Buddy
  • How To Build Web Service Using Spring Boot 2.x

Partner Resources

×

Comments
Oops! Something Went Wrong

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
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!