README.md

# howdy_authentication_cookies

[![Package Version](https://img.shields.io/hexpm/v/howdy_authentication_cookies)](https://hex.pm/packages/howdy_authentication_cookies)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/howdy_authentication_cookies/)

Cookie authentication library for the [Howdy](https://github.com/mikeyjones/howdy) Web Server, using ETS as session storage. 

## Quick start

```gleam
import gleam/erlang
import gleam/result
import gleam/string
import howdy/server
import howdy/context.{Context}
import howdy/context/user
import howdy/router.{Get, Post, RouterMap, RouterMapWithFilters}
import howdy/response
import howdy/filter
import howdy/authentication/cookie

pub fn main() {
  let routes =
    RouterMap(
      "/",
      routes: [
        Get("/", fn(_) { response.of_string("hello from root") }),
        Post("/signin", do_sign_in),
        Post(
          "/signout",
          fn(context) {
            cookie.sign_out(context, response.of_string("signed out"))
          },
        ),
        RouterMapWithFilters(
          "/secret",
          filters: [filter.authenticate(_, cookie.authenticate_with_cookie)],
          routes: [Get("/", get_secret_page)],
        ),
      ],
    )

  cookie.new()
  assert Ok(_) = server.start(routes)
  erlang.sleep_forever()
}

fn get_secret_page(context: Context(a)) {
  let email =
    context.user
    |> user.get_claim("email")
    |> result.unwrap("No Email")

  response.of_string(string.concat(["Email: ", email]))
}

fn do_sign_in(_context: Context(a)) {
  case cookie.sign_in(
    "username",
    "password",
    True,
    response.of_string("signed in!"),
    validate_user,
  ) {
    Ok(resp) -> resp
    Error(_) -> response.of_internal_error("failed to sign you in!")
  }
}

fn validate_user(
  _username: String,
  _password: String,
) -> Result(List(#(String, String)), Nil) {
  // TODO: Get the claims from somewhere, database maybe?
  Ok([#("email", "test@email.com")])
}
```

## Installation

If available on Hex this package can be added to your Gleam project:

```sh
gleam add howdy_authentication_cookies
```

and its documentation can be found at <https://hexdocs.pm/howdy_authentication_cookies>.


## Configuration

There are several configuration options that allows you to control
the cookie authentication procesess better. The default configuration
is great for getting started, but is not advised for production, and 
you should set stronger cookie attributes to prevent unauthorized access 
to you cookies.

Calling ```cookie.new()``` sets the authentication library to its defaults. 

### Setting Cookie Attributes

You can modify the attributes of the cookies with the following:

```gleam
cookie.new_with_config(
    CookieConfig(
        Some(Attributes(
            max_age: None,
            domain: None,
            path: None,
            secure: False,
            http_only: False,
            same_site: None,
        )),
        None,
        None))
```

### Setting a custom storage method:

In order to change the default session storage of ETS, you can override
3 functions. This would allow you to use a database, reddis or a shared
implitmention that multiple servers can use.

You will need to create 3 functions with the following signitures:

```gleam
// Insert (unique_key, username, timeout_in_seconds, claims)
fn(String, String, Int, List(#(String, String))) -> Bool, 

// Lookup input of (unique_key) returns a result of #(unique_key, username, timeout_in_seconds, claims) 
fn(String) -> Result(#(String, String, Int, List(#(String, String))), Nil),

// Delete (unique_key)
fn(String) -> Bool,
```

#### Example:

```gleam
fn insert(key, user, timeout, claims) {
    False
} 

fn lookup(key) {
    Error(Nil)
}

fn delete(key) {
    False
}

cookie.new_with_config(
    CookieConfig(
        None,
        Some(
            DataStorage(
                insert, 
                lookup, 
                delete
            )
        ),
        None))
```

### Config session timeout

By default, if `remember me` is set to `False`, the session will last 4 hours, if it is set to `True` it will last for 30 days. You can change these defaults with the the foolowing:

```gleam
cookie.new_with_config(
    CookieConfig(
        None,
        None,
        Some(
            Timeouts(
                long: 126144000, // 4 years
                short: 10 // 10 seconds
            )
        )
    )
)
```