Skip to content

Latest commit

 

History

History
172 lines (101 loc) · 6.42 KB

README.md

File metadata and controls

172 lines (101 loc) · 6.42 KB

clauth - OAuth 2 based simple authentication system for Clojure Ring

Build Status

This is a simple OAuth 2 provider that is designed to be used as a primary authentication provider for a Clojure Ring app.

It is under development by a Clojure novice. Please help give feedback on use of idiomatic clojure.

It currently only handles OAuth2 bearer authentication and interactive authentication.

The full OAuth 2 token authorization flow will be added shortly as everything else is now more or less working.

See draft-ietf-oauth-v2-bearer

The following bearer tokens are implemented:

Install

Add the following dependency to your project.clj file:

[clauth "1.0.0-beta2"]

Usage

There are currently 2 middlewares defined:

  • wrap-bearer-token
  • require-bearer-token!

Both of them take as a parameter a function which should return a object representing the token. This could be a user object, but could also be a token object with specific meta-data. I may standardize on something when more of the framework is developed.

The object returned by your function is set to :access-token entry in the request.

The difference between wrap-bearer-token and require-bearer-token! is that wrap will find a token but not require it. require-bearer-token will return a HTTP 401 header.

Grant Types

Currently the following Grant types are supported:

Grant types are implemented using multimethods. To implement one

(defmethod token-request-handler "my_grant_type" [req authenticator] ...)

Authorization request

We currently support the following authorization requests:

Tokens

There is a protocol defined called Expirable which implements one function:

(is-valid? token)

This is implementend by IPersistentMap so {} represents a valid token where {:expires (date-time 2011)} is invalid.

A OAuthToken record exists which can be instantiated and stored easily by the create-token function:

(create-token client user)

Client Applications

A ClientApplication record exists which can be instantiated and stored easily by the register-app function:

(register-app name url)

A client application has a client-id and a client-secret which is used for issuing tokens.

Users

A User record exists which can be instantiated and stored easily by the register-user function:

(register-user login password name url)

Stores

Stores are used to store tokens and will be used to store clients and users as well.

There is a generalized protocol called Store and currently a simple memory implementation used for it.

It should be pretty simple to implement this Store with redis, sql, datomic or what have you.

It includes a simple Redis implementation.

The stores used by the various parts are defined in an atom for each type. reset! each of them with your own implementation.

The following stores are currently defined:

  • token-store is in clauth.token/token-store
  • client-store is in clauth.client/client-store
  • user-store is in clauth.user/user-store

To use the redis store add the following to your code:

(reset! token-store (create-redis-store "tokens"))
(reset! client-store (create-redis-store "clients"))
(reset! user-store (create-redis-store "users"))

And wrap your handler with a redis connection middleware similar to this:

(defn wrap-redis-store [app]
  (fn [req]
    (redis/with-server
     {:host "127.0.0.1"
      :port 6379
      :db 14
     }
     (app req))))

Issuing OAuth Tokens

There is currently a single token-handler that provides token issuance called token-handler. Install it in your routes by convention at "/token" or "/oauth/token".

(defn routes [req]
  (case (req :uri)
    "/token" ((token-handler) req )
    ((require-bearer-token! handler) req)))

Using as primary user authentication on server

One of the ideas of this is using OAuth tokens together with traditional sessions based authentication providing the benefits of both. To do this we create a new token when a user logs in and adds it to the session.

Why is this a good idea?

  • You will be able to view a list of other sessions going on for security purposes
  • You will be able to remotely log of another session
  • Your app deals with tokens only. So this is also ideal for an API with a javascript front end

To use this make sure to wrap the session middleware. We have a login handler endpoint that could be used like this:

(defn routes [master-client]
  (fn [req]
  (case (req :uri)
    "/login" ((login-handler master-client) req )
    ((require-bearer-token! handler) req))))

The master-client is a client record representing your own application. A default login view is defined in clauth.views/login-form-handler but you can add your own. This just needs to be a ring handler presenting a form with the parameters "username" and "password".

(defn routes [master-client]
  (fn [req]
  (case (req :uri)
    "/login" ((login-handler my-own-login-form-handler master-client) req )
    ((require-bearer-token! handler) req))))

Run Demo App

A mini server demo is available. It creates a client for you and prints out instructions on how to issue tokens with curl.

lein run

TODO

The goal is to implement the full OAuth2 spec in this order:

License

Copyright (C) 2012 Pelle Braendgaard http://stakeventures.com

Distributed under the Eclipse Public License, the same as Clojure.