> ## Documentation Index
> Fetch the complete documentation index at: https://docs.shuttle.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Cookie Authentication

> Explore how you can secure your Actix Web application by using cookies.

## Description

This example shows how to use authentication within actix-web with cookies, assisted by actix-identity and actix-session.
The idea is that all requests authenticate first at the login route to get a cookie, then the cookie is sent with all requests requiring authentication using the HTTP cookie header.

You can clone the example below by running the following (you'll need `shuttle` CLI installed):

```bash theme={null}
shuttle init --from shuttle-hq/shuttle-examples --subfolder actix-web/cookie-authentication
```

Three Actix Web routes are registered in this file:

* `/public`: a route that can be called without needing any authentication.
* `/login`: a route for posting a JSON object with a username and password to get a cookie.
* `/private`: a route that will display whether you're logged in or not, based on if you're logged in.

The example uses `actix-identity` and `actix-session` with a cookie store to assist with easy setup.

## Code

```toml Cargo.toml theme={null}
[package]
name = "cookie-authentication"
version = "0.1.0"
edition = "2021"

[dependencies]
actix-identity = "0.7.1"
actix-session = { version = "0.9.0", features = ["cookie-session"] }
actix-web = "4.3.1"
shuttle-actix-web = "0.57.0"
shuttle-runtime = "0.57.0"
tokio = "1.26.0"
```

Your `main.rs` should look like this:

````rust main.rs theme={null}
use actix_identity::{Identity, IdentityMiddleware};
use actix_session::{config::PersistentSession, storage::CookieSessionStore, SessionMiddleware};
use actix_web::{
    cookie::{time::Duration, Key},
    error, get,
    http::StatusCode,
    middleware,
    web::{self, ServiceConfig},
    HttpMessage as _, HttpRequest, Responder,
};
use shuttle_actix_web::ShuttleActixWeb;

const FIVE_MINUTES: Duration = Duration::minutes(5);

#[get("/")]
async fn index(identity: Option<Identity>) -> actix_web::Result<impl Responder> {
    let id = match identity.map(|id| id.id()) {
        None => "anonymous".to_owned(),
        Some(Ok(id)) => id,
        Some(Err(err)) => return Err(error::ErrorInternalServerError(err)),
    };

    Ok(format!("Hello {id}"))
}

#[get("/login")]
async fn login(req: HttpRequest) -> impl Responder {
    // some kind of authentication should happen here

    // attach a verified user identity to the active session
    Identity::login(&req.extensions(), "user1".to_owned()).unwrap();

    web::Redirect::to("/").using_status_code(StatusCode::FOUND)
}

#[get("/logout")]
async fn logout(id: Identity) -> impl Responder {
    id.logout();

    web::Redirect::to("/").using_status_code(StatusCode::FOUND)
}

#[shuttle_runtime::main]
async fn main() -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> {
    // Generate a random secret key. Note that it is important to use a unique
    // secret key for every project. Anyone with access to the key can generate
    // authentication cookies for any user!
    //
    // When deployed the secret key should be read from deployment secrets.
    //
    // For example, a secure random key (in base64 format) can be generated with the OpenSSL CLI:
    // ```
    // openssl rand -base64 64
    // ```
    //
    // Then decoded and converted to a Key:
    // ```
    // let secret_key = Key::from(base64::decode(&private_key_base64).unwrap());
    // ```
    let secret_key = Key::generate();

    let config = move |cfg: &mut ServiceConfig| {
        cfg.service(
            web::scope("")
                .service(index)
                .service(login)
                .service(logout)
                .wrap(IdentityMiddleware::default())
                .wrap(
                    SessionMiddleware::builder(CookieSessionStore::default(), secret_key.clone())
                        .cookie_name("auth-example".to_owned())
                        .cookie_secure(false)
                        .session_lifecycle(PersistentSession::default().session_ttl(FIVE_MINUTES))
                        .build(),
                )
                .wrap(middleware::NormalizePath::trim())
                .wrap(middleware::Logger::default()),
        );
    };

    Ok(config.into())
}
````

## Usage

Once you've cloned this example, launch it locally by using `shuttle run`. Once you've verified that it's up, you'll now be able to go to `http://localhost:8000` and start trying the example out!

First, we should be able to access the public endpoint without any authentication using:

```sh theme={null}
curl http://localhost:8000/public
```

But trying to access the private endpoint will return "Hello anonymous":

```sh theme={null}
curl http://localhost:8000/private
```

So let's get a cookie from the login route first:

```sh theme={null}
curl http://localhost:8000/login
```

Accessing the private endpoint with the token will now succeed:

```sh theme={null}
curl --header "Authorization: Bearer <token>" http://localhost:8000/private
```

The token is set to expire in 5 minutes, so wait a while and try to access the private endpoint again. Once the token has expired, a user will need to get a new token from login.
Since tokens usually have a longer than 5 minutes expiration time, we can create a `/refresh` endpoint that takes an active token and returns a new token with a refreshed expiration time.

Looking to extend this example? Here's a couple of ideas to get you started:

* Create a frontend to host the login
* Add a route for registering
* Use a database to check login credentials

***

<Snippet file="other-frameworks.mdx" />

<Snippet file="check-examples.mdx" />
