README.md

# Felix

Allows user to create a new account or log in by providing a passkey.
WebAuthN/Fido2 integration.

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

## JS only

This library is bindings for the [SimpleWebAuthn server package](https://github.com/MasterKale/SimpleWebAuthn/).
Therefore it only works on JS server environments.

*I have a prototype for pure Gleam implementation but it is incomplete, get in touch if you need the use of an erlang based implementation.*

## Usage

```sh
npm i --save @simplewebauthn/server
gleam add felix@1
```

### Verify registration

```gleam
import felix/server

pub fn verify_registration(response: String) {
  // generated for each registration and sent to the client earlier
  let expected_challenge =
    bit_array.base64_url_encode(<<"secret challenge">>, False)

  use #(verified, info) <- promise.map_try(server.verify_registration(
    response: response,
    expected_challenge: expected_challenge,
    expected_origin: "http://localhost:8080",
    // rpid matches origin without scheme and port
    expected_rpid: "localhost",
    require_user_presence: False,
    require_user_verification: False,
  ))
  case verified {
    True ->
      // save the info.public_key in your database
      Ok(Nil)
    False -> Error("invalid")
  }
}
```

**NOTE:** client side registration can be handled using the [plinth](https://github.com/crowdhailer/plinth) library bindings to the Web credentials API.
```gleam
import plinth/browser/credentials
import plinth/browser/credentials/public_key

pub fn main(){
  let assert Ok(container) = credentials.from_navigator()
  let options =
    public_key.creation(
      <<"secret challenge">>,
      public_key.ES256,
      "New service",
      <<"my user id">>,
      "bob@example.com",
      "Bob",
    )
  use response <- promise.try_await(public_key.create(container, options))
  let response = json.to_string(public_key.to_json(response))
  // send to server to validate_registration
}
```

### Verify authentication

```gleam
import felix/server

pub fn verify_authentication_test(response) {
  // generated for each authentication
  let expected_challenge =
    bit_array.base64_url_encode(<<"another secret">>, False)
  let public_key = todo as "pull from DB"

  use #(verified, info) <- promise.map_try(server.verify_authentication(
    response: response,
    expected_challenge: expected_challenge,
    expected_origin: "http://localhost:8080",
    expected_rpid: "localhost",
    public_key: public_key,
    require_user_verification: False,
  ))
  verified
  |> should.be_true
  info.user_verified
  |> should.be_true
  info.credential_device_type
  |> should.equal(server.MultiDevice)
  info.credential_backed_up
  |> should.be_true
  info.origin
  |> should.equal("http://localhost:8080")
  info.rp_id
  |> should.equal("localhost")
  Ok(Nil)
}
```
**NOTE:** client side registration can be handled using the [plinth](https://github.com/crowdhailer/plinth) library bindings to the Web credentials API.
```gleam
import plinth/browser/credentials
import plinth/browser/credentials/public_key

pub fn main(){
  let assert Ok(container) = credentials.from_navigator()
  let options = public_key.request(<<"another secret">>)
  use response <- promise.try_await(public_key.get(container, options))
  let response = json.to_string(public_key.to_json(response))
  // send to server to validate_authentication
}
```

---

Further documentation can be found at <https://hexdocs.pm/felix>.

## Development

```sh
gleam run   # Run the project
gleam test  # Run the tests
```

## Credit

Created for [EYG](https://eyg.run/), a new integration focused programming language.