Basic and Form authentication

Ktor supports two methods of authentication with the user and raw password as credentials:basic and form.

  1. install(Authentication) {
  2. basic(name = "myauth1") {
  3. realm = "Ktor Server"
  4. validate { credentials -> /*...*/ }
  5. }
  6. form(name = "myauth2") {
  7. userParamName = "user"
  8. passwordParamName = "password"
  9. challenge = FormAuthChallenge.Unauthorized
  10. validate { credentials -> /*...*/ }
  11. }
  12. }

Both authentication providers have a method validate to provide a callback that must generate a Principal from given a UserPasswordCredentialor null for invalid credentials. That callback is marked as suspending, so that you can validate credentials in an asynchronous fashion.

You can use several strategies for validating:

Strategy: Manual credential validation

Since there is a validate callback for authentication, you can just put your code there.So you can do things like checking the password against a constant, authenticating using a databaseor composing several validation mechanisms.

  1. application.install(Authentication) {
  2. basic("authName") {
  3. realm = "ktor"
  4. validate { credentials ->
  5. if (credentials.password == "${credentials.name}123") UserIdPrincipal(credentials.name) else null
  6. }
  7. }
  8. }

Remember that both the name and the password from the credentials are arbitrary values.Remember to escape and/or validate them when accessing with those values to the file system, a database,when storing them, or generating HTML with its content, etc.

Strategy: Validating using UserHashedTableAuth

There is a class that handles hashed passwords in-memory to authenticate UserPasswordCredential.You can populate it from constants in code or from another source. You can use predefined digest functionsor your own.

Instantiating:

  1. val userTable = UserHashedTableAuth(getDigestFunction("SHA-256", salt = "ktor"), mapOf(
  2. "test" to decodeBase64("VltM4nfheqcJSyH887H+4NEOm2tDuKCl83p5axYXlF0=") // sha256 for "test"
  3. ))

Configuring server/routes:

  1. application.install(Authentication) {
  2. basic("authName") {
  3. realm = "ktor"
  4. authenticate { credentials -> userTable.authenticate(credentials) }
  5. }
  6. }

The idea here is that you are not storing the actual password but a hash, so even if your data source is leaked,the passwords are not directly compromised. Though keep in mind that when using poor passwords and weak hashing algorithmsit is possible to do brute-force attacks. You can append (instead of prepend) long salt values and do multiple hashstages or do key derivate functions to increase security and make brute-force attacks non-viable.You can also enforce or encourage strong passwords when creating users.