reblocks-auth - A system to add an authentication to the Reblocks based web-site.

REBLOCKS-AUTH ASDF System Details

Reblocks-auth is a system for adding authentication for your Reblocks application. It allows users to login using multiple ways. Right now GitHub is only supported but the list will be extended.

This system uses Mito as a storage to store data about users and their data from service providers. Each user has a unique nickname and an optional email. Also, one or more identity providers can be bound to each user account.

Installation

You can install this library from Quicklisp, but you want to receive updates quickly, then install it from Ultralisp.org:

(ql-dist:install-dist "http://dist.ultralisp.org/"
                      :prompt nil)
(ql:quickload :reblocks-auth)

Example App

I've made an example application to demonstrate how does reblocks-auth system work. To start this example application, run this code in the REPL:

(asdf:load-system :reblocks-auth-example)

(reblocks-auth-example/server:start :port 8080)

When you'll open the http://localhost:8080/ you will see this simple website:

Usage

This system provides a way for user authentifications. Each user is represented in the database using reblocks-auth/models:user model user can be bound to one or more "social profiles" - reblocks-auth/models:social-profile. For example, if user logged in via GitHub, then database will store one "user" record and one "social-profile" record. Each social profile can hold additional information in it's metadata slot.

To use this system, you have to define two routes which will be responsible for login and logout. On each route you have to render either reblocks-auth:login-processor or reblocks-auth:logout-processor widgets.

Usually you can define your routes like this (reblocks-navigation-widget:defroutes is used here):

(defroutes routes
    ("/" (make-page-frame
          (make-landing-page)))
  ("/login"
   (make-page-frame
    (reblocks-auth:make-login-processor)))
  ("/logout"
   (make-page-frame
    (reblocks-auth:make-logout-processor))))

This code will render a set up buttons to login through enabled service providers. Enabled service providers are listed in reblocks-auth:*enabled-services* variable.

Login processor does two things:

Logout processor renders a "logout" button and when user clicks on it, removes user from the current session.

Telegram Authentication

Telegram authentication uses the Telegram Login Widget. When a user clicks the widget, Telegram opens a confirmation dialog and then redirects back to your server with signed user data. The server verifies the HMAC-SHA256 signature using the bot token before accepting the login.

Setting Up a Bot

  1. Open @BotFather in Telegram and send /newbot.

  2. After the bot is created, send /setdomain and enter your website's domain (e.g. example.com). Telegram will reject logins from any other domain.

  3. Copy the bot username (without @) and the bot token shown by BotFather.

Configuration

(setf reblocks-auth/providers/telegram:*bot-username* "MyAppBot")
(setf reblocks-auth/providers/telegram:*bot-token*
      (secret-values:conceal-value "123456:ABC-DEF..."))

(pushnew :telegram reblocks-auth:*enabled-services*)

Two variables control the provider:

How the Login Flow Works

  1. The widget script is rendered as a <script> tag with data-auth-url pointing to /login?service=telegram.

  2. When a user clicks the widget, Telegram opens a confirmation dialog.

  3. On success Telegram appends id, first_name, last_name, username, photo_url, auth_date, and hash to the data-auth-url and redirects the browser there.

  4. The login-processor widget picks up the service=telegram parameter and calls reblocks-auth/auth:authenticate.

  5. The server verifies the HMAC-SHA256 signature and checks that auth_date is no older than 24 hours.

  6. The user record is looked up or created and stored in the session.

Testing on Localhost

Telegram requires a real public domain — localhost is not accepted. For local development, expose your server with a tunneling tool and register that domain with BotFather:

# ngrok (free tier gives a random HTTPS URL each run)
ngrok http 8080

# localtunnel (fixed subdomain)
npx localtunnel --port 8080 --subdomain myapp

Then in BotFather send /setdomain with the tunnel hostname (e.g. myapp.loca.lt). Open the app through the tunnel URL rather than localhost:8080.

Yandex SmartCaptcha Support

Yandex SmartCaptcha provides an alternative to Google reCAPTCHA for protecting the email-based authentication form.

Quick Start

To enable Yandex SmartCaptcha, set following variables:

(setf reblocks-auth/providers/email/processing:*smartcaptcha-client-key* "your-client-key")
(setf reblocks-auth/providers/email/processing:*smartcaptcha-server-key* "your-server-key")

Getting Yandex SmartCaptcha Keys

  1. Go to Yandex Cloud Console

  2. Navigate to SmartCaptcha service

  3. Create a new SmartCaptcha site

  4. Copy Client key (for frontend) and Server key (for backend)

  5. Add keys to your application configuration

How It Works

  1. User clicks "Email" login button

  2. JavaScript loads https://smartcaptcha.yandexcloud.net/captcha.js

  3. User solves captcha (automatic or manual)

  4. Token is generated and sent to backend

  5. Backend validates token via https://smartcaptcha.yandexcloud.net/validate

  6. Email with login code is sent

  7. User enters code to complete authentication

Multiple Captcha Providers

Yandex SmartCaptcha takes precedence when both Yandex SmartCaptcha and Google reCAPTCHA are configured.

If only Google reCAPTCHA is configured, it will be used.

Verification

The server validates the captcha token via the verify-smartcaptcha function, which POSTs the token and server key to Yandex's validation endpoint.

Successful validation returns {"status": "ok"}. Failed validation returns {"status": "error"}.

API

REBLOCKS-AUTH/AUTH

Generics

generic-function
service &rest params &key id first\_name last\_name username photo\_url auth\_date hash retpath code

Called when user had authenticated in the service and returned to our site.

All GET arguments are collected into a plist and passed as params.

Should return two values a user and a flag denotifing if user was just created.

REBLOCKS-AUTH/BUTTON

Generics

Renders a button for given service. Service should be a keyword like :github or :facebook.

REBLOCKS-AUTH/CONDITIONS

Classes

UNABLE-TO-AUTHENTICATE

Readers

REBLOCKS-AUTH/CORE

Classes

LOGIN-PROCESSOR

This widget should be rendered to process user's login.

LOGOUT-PROCESSOR

This widget should be rendered to process user's logout.

Generics

By default, renders a list of buttons for each allowed authentication method.

Functions

Renders a row of buttons for enabled service providers.

Optionally you can specify RETPATH argument with an URI to return user after login.

Variables

When True, a new account will be created. Otherwise only already existing users can log in.

Set this variable to limit a services available to login through.

Append a funcallable handlers which accept single argument - logged user.

REBLOCKS-AUTH/ERRORS

Classes

NICKNAME-IS-NOT-AVAILABLE

Signalled when there is already a user with given nickname.

REBLOCKS-AUTH/GITHUB

Functions

Returns current user's scopes.

Returns current user's GitHub token.

function
&KEY (CLASS "button small") (SCOPES \*DEFAULT-SCOPES\*) (TEXT "Grant permissions") (RETPATH (GET-URI))

Renders a button to request more scopes.

Variables

A listo of default scopes to request from GitHub.

OAuth secret. It might be a string or secret-values:secret-value.

REBLOCKS-AUTH/MODELS

Classes

SOCIAL-PROFILE

Represents a User's link to a social service. User can be bound to multiple social services.

Readers

A hash table with lowercased strings as a key and values given from the authentication plroviders.s

A user instance, bound to the social-profile.

Accessors

A hash table with lowercased strings as a key and values given from the authentication plroviders.s

USER

This class stores basic information about user - it's nickname and email. Additional information is stored inside social-profile instances.

Readers

Functions

Changes nickname of the current user.

function
service service-user-id &rest kwargs &key email first-name last-name username photo-url

Returns a user with given email.

Returns a user with given email.

Returns a list of social profiles, bound to the user.

Variables

Allows to redefine a model, for users to be created by the reblocks-auth.

REBLOCKS-AUTH/PROVIDERS/EMAIL/MAILGUN

Macros

macro
NAME (FROM-EMAIL URL-VAR &KEY (SUBJECT "Authentication code")) &BODY HTML-TEMPLATE-BODY

REBLOCKS-AUTH/PROVIDERS/EMAIL/MODELS

Classes

REGISTRATION-CODE

This model stores a code sent to an email for signup or log in.

Readers

Functions

Usually you should define a global callback using reblocks-auth/providers/email/mailgun:define-code-sender macro, but you can provide an alternative function to handle email sending.

Variables

Set this variable to a function of one argument of class registration-code. It should send a registration code using template, suitable for your website.

REBLOCKS-AUTH/PROVIDERS/EMAIL/PROCESSING

Classes

REQUEST-CODE-FORM

Readers

Accessors

Generics

Variables

Set this variable to a secret key, generated by Google reCaptcha.

Set this variable to a site key, generated by Google reCaptcha.

Set this variable to a client key, generated by Yandex SmartCaptcha.

Set this variable to a server key, generated by Yandex SmartCaptcha.

REBLOCKS-AUTH/PROVIDERS/EMAIL/RESEND

Functions

Makes a function which will prepare params and call THUNK function with email and URL.

Usually you don't need to call this function directly and you can use just define-code-sender macro.

Macros

macro
NAME (FROM-EMAIL URL-VAR &KEY (SUBJECT "Authentication code")) &BODY HTML-TEMPLATE-BODY

REBLOCKS-AUTH/PROVIDERS/TELEGRAM

Functions

Renders the Telegram Login Widget script tag.

Variables

Telegram bot token used to verify authentication data. Can be a string or secret-values:secret-value.

Telegram bot username (without @). Required for the login widget.

Roadmap