Giter Site home page Giter Site logo

clauth's Introduction

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 currently handles OAuth2 bearer authentication and interactive authentication.

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-rc17"]

Possible breaking change with 1.0.0-rc14

The oauth models have been refactored to be simple maps instead of records. The factory functions are simpler and take a simple map of data.

If you have your own token and authcode creator functions for versions prior to 1.0.0-rc14 please verify that they work when upgrading.

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:

You can control the ones you wish to support by using the configuration parameter :allowed-response-types to the authorization-handler

(authorization-handler {:allowed-response-types ["code"]}) ;; Only support Authorization Code Grants

Implement custom authorization requests:

(defmethod authorization-request-handler "custom"
  ... )

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 map can be instantiated and stored easily by the create-token function:

(create-token client user)

Client Applications

A ClientApplication map 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 map 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 based on Carmine.

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
  • auth-code-store is in clauth.auth-code/auth-code-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:

(def server-conn
  {:pool {}
   :spec {:host "127.0.0.1"
          :port 6379
          :db 14}})

(reset! token-store (create-redis-store "tokens" server-conn))
(reset! auth-code-store (create-redis-store "auth-codes" server-conn))
(reset! client-store (create-redis-store "clients" server-conn))
(reset! user-store (create-redis-store "users" server-conn))

Authorization OAuth Tokens

There is currently a single authorization-handler that handles authorization called authorization-handler. Install it in your routes by convention at "/authorize" or "/oauth/authorize".

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

Authorization handler comes with defaults that use the various built in token, user etc. stores. You can override these by passing in a configuration map containing functions.

(authorization-handler {:authorization-form authorization-form-handler
                        :client-lookup clauth.client/fetch-client
                        :token-lookup clauth.token/fetch-token
                        :token-creator clauth.token/create-token
                        :auth-code-creator clauth.auth-code/create-auth-code})

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)))

Token handler comes with defaults that use the various built in token, user etc. stores. You can override these by passing in a configuration map containing functions.

(token-handler {:client-authenticator clauth.client/authenticate-client
                :user-authenticator clauth.user/authenticate-user
                :token-creator clauth.token/create-token
                :auth-code-revoker clauth.auth-code/revoke-auth-code!
                :auth-code-lookup clauth.auth-code/fetch-auth-code })

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 -m clauth.demo

TODO

The goal is to implement the full OAuth2 spec. The only main feature missing is. I'll aim for that for 1.1 as most people currently don't use refresh tokens:

Contribute

You will need to have a Redis database running in the background in order to have some of the tests pass, otherwise, you will get an error about the connection being refused.

If you have Homebrew on Mac OSX, you can get Redis by typing brew install redis in the command line. Once that's done, get the Redis database started in your Terminal window by typing the following:

redis-server /usr/local/etc/redis.conf

License

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

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

clauth's People

Contributors

pelle avatar rplevy-draker avatar dyba avatar ryfow avatar sparkhom avatar brunchboy avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.