• Stars
    star
    123
  • Rank 290,145 (Top 6 %)
  • Language
    PHP
  • Created almost 15 years ago
  • Updated almost 11 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Auth for people who hate the Auth component

Authsome Plugin

Auth for people who hate the Auth component

Background

Authsome is a CakePHP 2.x plugin that makes authentication a pleasure to work with by following a few simple rules:

Assume nothing: Authsome requires that you have some kind of user model, but that's it. It doesn't care if you use a database, passwords or religious ceremonies for verifying your member logins.

Touch nothing: Authsome does not interact with your application at all. No login redirects, no permissions checks, nothing. You never have to worry about the underlaying magic, it will never get into your way.

Always available: Authsome is there for you when you need it. You can do stuff like Authsome::get('id') from anywhere in your project. If you have MVC OCD, you can also use Authsome as a regular component: $this->Authsome->get('id')

Requirements

  • PHP 5.2.8+
  • CakePHP 2.x

Installation

For 1.3 support, please see the 1.3 branch;

[Manual]

[GIT Submodule]

In your app directory type:

git submodule add git://github.com/felixge/cakephp-authsome.git Plugin/Authsome
git submodule init
git submodule update

[GIT Clone]

In your plugin directory type

git clone git://github.com/felixge/cakephp-authsome.git Authsome

Enable plugin

In 2.0 you need to enable the plugin your app/Config/bootstrap.php file:

CakePlugin::load('Authsome');

If you are already using CakePlugin::loadAll();, then this is not necessary.

Usage

Once installed, load authsome in your AppController and specify the name of your user model:

class AppController extends Controller {
	public $components = array(
		'Authsome.Authsome' => array(
			'model' => 'User'
		)
	);
}

Implement authsomeLogin in your user model (must return a non-null value):

class User extends AppModel{
	public function authsomeLogin($type, $credentials = array()) {
		switch ($type) {
			case 'guest':
				// You can return any non-null value here, if you don't
				// have a guest account, just return an empty array
				return array('it' => 'works');
			case 'credentials':
				$password = Authsome::hash($credentials['password']);

				// This is the logic for validating the login
				$conditions = array(
					'User.email' => $credentials['email'],
					'User.password' => $password,
				);
				break;
			default:
				return null;
		}

		return $this->find('first', compact('conditions'));
	}
}

Almost done! Check if you did everything right so far by putting this in one of your controllers:

$guest = Authsome::get();
debug($guest);

If this returns Array([it] => works), you can go ahead and implement a simple login function:

class UsersController extends AppController{
	public function login() {
		if (empty($this->data)) {
			return;
		}

		$user = Authsome::login($this->data['User']);

		if (!$user) {
			$this->Session->setFlash('Unknown user or wrong password');
			return;
		}

		$user = Authsome::get();
		debug($user);
	}
}

And add a app/views/users/login.ctp file like this:

<h2><?php echo $this->pageTitle = 'Login'; ?></h2>
<?php
echo $form->create('User', array('action' => $this->action));
echo $form->input('email', array('label' => 'Email'));
echo $form->input('password', array('label' => "Password"));
echo $form->submit('Login');
echo $form->end();
?>

The array passed into Authsome::login() gets passed directly to your authsomeLogin function, so you really pass any kind of credentials. You can even come up with your own authentication types by doing Authsome::login('voodoo_auth', $chickenBones).

Cookies

Any login created by Authsome::login() will only last as long as your CakePHP session itself. However, you might want to offer one of those nifty "Remember me for 2 weeks" buttons. Authsome::persist() comes to rescue!

First of all change your login action like this:

public function login() {
	if (empty($this->data)) {
		return;
	}

	$user = Authsome::login($this->data['User']);

	if (!$user) {
		$this->Session->setFlash('Unknown user or wrong password');
		return;
	}

	$remember = (!empty($this->data['User']['remember']));
	if ($remember) {
		Authsome::persist('2 weeks');
	}
}

Also add a checkbox like this to your form:

echo $form->input('remember', array(
	'label' => "Remember me for 2 weeks",
	'type' => "checkbox"
));

Authsome itself does not care how you manage your cookie login tokens for auth persistence, but I highly recommend following Charles' Receipe for this. Charles recommends to create a table that maps user_ids and login tokens, here is what I use:

CREATE TABLE `login_tokens` (
  `id` int(11) NOT NULL auto_increment,
  `user_id` int(11) NOT NULL,
  `token` char(32) NOT NULL,
  `duration` varchar(32) NOT NULL,
  `used` tinyint(1) NOT NULL default '0',
  `created` datetime NOT NULL,
  `expires` datetime NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

Don't forget to create an empty model in app/models/login_token.php for this:

class LoginToken extends AppModel{
}

Next you'll need to implement authsomePersist in your user model, which creates and stores a unique login token when Authsome::persist() is called:

public $hasMany = array('LoginToken');

public function authsomePersist($user, $duration) {
	$token = md5(uniqid(mt_rand(), true));
	$userId = $user['User']['id'];

	$this->LoginToken->create(array(
		'user_id' => $userId,
		'token' => $token,
		'duration' => $duration,
		'expires' => date('Y-m-d H:i:s', strtotime($duration)),
	));
	$this->LoginToken->save();

	return "${token}:${userId}";
}

So far so good. If you are still on track, you should now be able to see new records showing up in your login_tokens table if you log in with the remember checkbox checked.

If so, proceed to the next step and add the 'cookie' login $type to your authsomeLogin function:

public function authsomeLogin($type, $credentials = array()) {
	switch ($type) {
		case 'guest':
			// You can return any non-null value here, if you don't
			// have a guest account, just return an empty array
			return array('it' => 'works');
		case 'credentials':
			$password = Authsome::hash($credentials['password']);

			// This is the logic for validating the login
			$conditions = array(
				'User.email' => $credentials['email'],
				'User.password' => $password,
			);
			break;
		case 'cookie':
			list($token, $userId) = split(':', $credentials['token']);
			$duration = $credentials['duration'];

			$loginToken = $this->LoginToken->find('first', array(
				'conditions' => array(
					'user_id' => $userId,
					'token' => $token,
					'duration' => $duration,
					'used' => false,
					'expires <=' => date('Y-m-d H:i:s', strtotime($duration)),
				),
				'contain' => false
			));

			if (!$loginToken) {
				return false;
			}

			$loginToken['LoginToken']['used'] = true;
			$this->LoginToken->save($loginToken);

			$conditions = array(
				'User.id' => $loginToken['LoginToken']['user_id']
			);
			break;
		default:
			return null;
	}

	return $this->find('first', compact('conditions'));
}

Let's go over this real quick. First we are checking the db for a matching token. If none is found, we return false. If we find a valid token, we invalidate it and set the conditions for finding the user that belongs to the token.

Pretty simple! You could also do this entirely different. For example you could skip having a login_tokens table all together and instead give out tokens that are signed with a secret and a timestamp. However, the drawback with those tokens is that they could be used multiple times which makes cookie theft a more severe problem.

Security Advisory: You should require users to re-authenticate using an alternative login method in case of the following:

  • Changing the user's password
  • Changing the user's email address (especially if email-based password recovery is used)
  • Any access to the user's address, payment details or financial information
  • Any ability to make a purchase

This can easily be done by tweaking the end of your authsomeLogin function like this:

$user = $this->find('first', compact('conditions'));
if (!$user) {
	return false;
}
$user['User']['loginType'] = $type;
return $user;

Then deny access to any of the functionality mentioned above like this:

if (Authsome::get('loginType') === 'cookie') {
	Authsome::logout();
	$this->redirect(array(
		'controller' => 'users',
		'action' => 'login',
	))
}

Under the hood

Authsome builds on a fairly simple logic. The first time you call Authsome::get(), it tries to find out who the active user it. This is done as follows:

  1. Check if Configure::read($this->settings['configureKey']) for a user record
  2. Check $this->Session->read($this->settings['sessionKey']) for a user record
  3. Check $this->Cookie->read($this->settings['cookieKey']) for a token

If all 3 of those checks do not produce a valid user record, authsome calls the user models authsomeLogin('guest') function and takes the record returned from that. If even that fails, authsome will throw an exception and bring your app to a crashing halt.

Options

AuthsomeComponent::initialize($controller, $settings)

Initializes the AuthsomeComponent with the given settings. This method is called for you when including Authsome in your AppController:

public $components = array(
	'Authsome.Authsome' => array(
		'model' => 'User'
	)
);

Available $settings and their defaults:

'model' => 'User',
// Those all default to $settings['model'] if not set explicitly
'configureKey' => null,
'sessionKey' => null,
'cookieKey' => null,

AuthsomeComponent::get($field = null)

Returns the current user record. If $field is given, the records sub-field for the main model is extracted. The following two calls are identical:

$this->Authsome->get('id');
$this->Authsome->get('User.id');

However, you could can also access any associations you may habe returned from your user models authsomeLogin function:

$this->Authsome->get('Role.name');

AuthsomeComponent::login($type = 'credentials', $credentials = null)

Passes the given $type and $credentials to your user model authsomeLogin function. Returns false on failure, or the user record on success.

If you skip the $type parameter, the default will be 'credentials'. This means the following two calls are identical:

$user = $this->Authsome->login('credentials', $this->data);
$user = $this->Authsome->login($this->data);

AuthsomeComponent::logout()

Destroys the current authsome session and also deletes any authsome cookies.

AuthsomeComponent::persist($duration = '2 weeks')

Calls the user models authsomePersist function to get a login token and stores it in a cookie. $duration must be a relative time string that can be parsed by strtotime() and must not be an absolute date or timestamp.

When performing a cookie login, authsome will automatically renew the login cookie for the given $duration again.

AuthsomeComponent::hash($passwords)

Takes the given $passwords and returns the sha1 hash for it using core.php's 'Security.salt' setting. The following two lines are identical:

$hashedPw = $this->Authsome->hash('foobar');
$hashedPw = Security::hash('foobar', 'sha1', true);

This is a convenience function. It is not used by Authsome internally, you are free to use any password hashing schema you desire.

Static convenience functions

The following static shortcuts exist for your convenience:

Authsome::get()
Authsome::login()
Authsome::logout()
Authsome::persist()
Authsome::hash()

They are identical to calling the AuthsomeComponent in your controller, but allow you to access Authsome anywhere in your app (models, views, etc.). If you suffer from MVC OCD, do not use these functions.

Sponsors

The initial development of Authsome was paid for by ThreeLeaf Creative, the makers of a fantastic CakePHP CMS system.

Authsome is developed by Debuggable Ltd. Get in touch if you need help making your next project an authsome one!

License

Copyright (c) 2009-2012 Felix Geisendรถrfer

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

More Repositories

1

node-style-guide

A guide for styling your node.js / JavaScript code. Fork & adjust to your taste.
JavaScript
4,950
star
2

fgprof

๐Ÿš€ fgprof is a sampling Go profiler that allows you to analyze On-CPU as well as Off-CPU (e.g. I/O) time together.
Go
2,469
star
3

node-ar-drone

A node.js client for controlling Parrot AR Drone 2.0 quad-copters.
JavaScript
1,755
star
4

node-dateformat

A node.js package for Steven Levithan's excellent dateFormat() function.
JavaScript
1,297
star
5

node-memory-leak-tutorial

A tutorial for debugging memory leaks in node
JavaScript
909
star
6

httpsnoop

Package httpsnoop provides an easy way to capture http related metrics (i.e. response time, bytes written, and http status code) from your application's http.Handlers.
Go
891
star
7

fgtrace

fgtrace is an experimental profiler/tracer that is capturing wallclock timelines for each goroutine. It's very similar to the Chrome profiler.
Go
878
star
8

faster-than-c

Talk outline: Faster than C? Parsing binary data in JavaScript.
JavaScript
836
star
9

node-dirty

A tiny & fast key value store with append-only disk log. Ideal for apps with < 1 million records.
JavaScript
625
star
10

node-stack-trace

Get v8 stack traces as an array of CallSite objects.
JavaScript
449
star
11

nodeguide.com

My unofficial and opinionated guide to node.js.
CSS
371
star
12

node-couchdb

A new CouchDB module following node.js idioms
JavaScript
364
star
13

sqlbench

sqlbench measures and compares the execution time of one or more SQL queries.
Go
361
star
14

node-sandboxed-module

A sandboxed node.js module loader that lets you inject dependencies into your modules.
JavaScript
344
star
15

node-require-all

An easy way to require all files within a directory.
JavaScript
300
star
16

tcpkeepalive

Go package tcpkeepalive implements additional TCP keepalive control beyond what is currently offered by the net pkg.
Go
238
star
17

node-paperboy

A node.js module for delivering static files.
JavaScript
234
star
18

godrone

GoDrone is a free software alternative firmware for the Parrot AR Drone 2.0.
Go
204
star
19

node-romulus

Building static empires with node.js.
JavaScript
157
star
20

node-gently

A node.js module that helps with stubbing and behavior verification.
JavaScript
142
star
21

node-combined-stream

A stream that emits multiple other streams one after another.
JavaScript
142
star
22

pprofutils

Go
122
star
23

node-growing-file

A readable file stream for files that are growing.
JavaScript
106
star
24

node-graphite

A node.js client for graphite.
JavaScript
105
star
25

node-cross-compiler

Simplified cross compiling for node.js using vagrant.
Shell
105
star
26

pidctrl

A PID controller implementation in Golang.
Go
96
star
27

node-m3u

A node.js module for creating m3u / m3u8 files.
JavaScript
89
star
28

debuggable-scraps

MIT licensed code without warranty ; )
PHP
79
star
29

traceutils

Code for decoding and encoding runtime/trace files as well as useful functionality implemented on top.
Go
62
star
30

node-delayed-stream

Buffers events from a stream until you are ready to handle them.
JavaScript
56
star
31

go-redis

A redis implementation written in Go.
Go
53
star
32

nodelog

A node.js irc bot that logs a channel
JavaScript
49
star
33

flame-explain

A PostgreSQL EXPLAIN ANALYZE visualizer with advanced quirk correction algorithms.
TypeScript
46
star
34

node-stream-cache

A simple way to cache and replay readable streams.
JavaScript
45
star
35

node-utest

The minimal unit testing library.
JavaScript
42
star
36

go-cpu-utilization

Go
39
star
37

go-xxd

The history of this repo demonstrates how to take a slow xxd implementation in Go, and make it faster than the native version on OSX/Linux.
Go
38
star
38

vim-nodejs-errorformat

Vim Script
36
star
39

tweets

C
35
star
40

go-ardrone

Parrot AR Drone 2.0 drivers and protocols written in Go.
Go
33
star
41

dotfiles

My setup. Pick what you like.
Lua
31
star
42

node-buffy

A module to read / write binary data and streams.
JavaScript
31
star
43

node-urun

The minimal test runner.
JavaScript
31
star
44

node-multipart-parser

A fast and streaming multipart parser.
JavaScript
30
star
45

node-require-like

Generates require functions that act as if they were operating in a given path.
JavaScript
29
star
46

benchmore

Go
28
star
47

node-nix

Node.js bindings for non-portable *nix functions
JavaScript
28
star
48

node-fake

Test one thing at a time, fake the rest.
JavaScript
28
star
49

node-bash

Utilities for using bash from node.js.
JavaScript
25
star
50

gounwind

Experimental go stack unwinding using frame pointers.
Go
25
star
51

node-microtest

Unit testing done right.
JavaScript
23
star
52

pgmigrate

pgmigrate implements a minimalistic migration library for postgres.
Go
22
star
53

node-comment

Proof of concept - Long polling message queue with CouchDB for persistence.
JavaScript
21
star
54

node-ugly

A hack so unbelievably ugly, yet so hard to resist
JavaScript
20
star
55

advent-2021

Advent of Go Profiling 2021.
Go
19
star
56

open-source-contribution-guide

A guide for anybody interested in contribution to my open source projects.
18
star
57

go-patch-overlay

WIP
Go
17
star
58

node-channel

A general purpose comet server written in node.js
JavaScript
16
star
59

node-active-x-obfuscator

A module to (safely) obfuscate all occurrences of the string 'ActiveX' inside any JavaScript code.
JavaScript
16
star
60

gotraceanalyzer

Command gotraceanalyzer turns golang tracebacks into useful summaries.
Go
14
star
61

go-observability-bench

Measure the overheads of various observability tools, especially profilers.
Jupyter Notebook
14
star
62

rebel-resize

Dynamic image resizing server written during my web rebels 2012 live coding.
JavaScript
13
star
63

node-fast-or-slow

Are your tests fast or slow? A pragmatic testing framework.
JavaScript
13
star
64

cl

Quickly clone git repositories into a nested folders like GOPATH.
Go
13
star
65

node-lazy-socket

A stateless socket that always lets you write().
JavaScript
13
star
66

raleigh-workshop-08

Code repository for the Raleigh, NC CakePHP workshop
PHP
12
star
67

node-deferred

Dojo deferreds as a nodejs module - Work in Progress
JavaScript
12
star
68

node-oop

Simple & light-weight oop.
JavaScript
11
star
69

node-win-iap

Verifies windows store receipts.
JavaScript
10
star
70

goardronefirmware

Open source firmware for the Parrot AR Drone 2.0 written in Go.
Go
10
star
71

node-far

https://github.com/felixge/node-far
JavaScript
10
star
72

node-convert-example

Node.js image resizing demo. One version with and one version without in-memory caching.
10
star
73

couchdb-benchmarks

some benchmark scripts for testing CouchDB performance
PHP
10
star
74

node-socketio-benchmark

A WebSocket / LongPolling simulation to estimate users / core
JavaScript
9
star
75

gpac

Mirror of https://gpac.svn.sourceforge.net/svnroot/gpac/trunk/gpac + my patches
C
9
star
76

node-passthrough-stream

An example of a passthrough stream for node.js
JavaScript
9
star
77

node-http-recorder

A little tool to record and replay http requests.
JavaScript
9
star
78

node-cluster-isolatable

Isolate workers so they only handle one request at a time. Useful for file uploads.
JavaScript
8
star
79

nodecopter-ssh-tunnel

Bash scripts for controlling an AR Drone over the internet via ssh tunneling.
Shell
8
star
80

makefs

WIP - come back later.
Go
8
star
81

node-unicode-sanitize

JavaScript
8
star
82

felixge.de

My site and blog.
HTML
7
star
83

dump

A code dump of things not worth putting into their own repo.
Go
7
star
84

ooti

A kickass test suite for node.js
JavaScript
6
star
85

go-cgo-finalizer

Demonstrates using runtime.SetFinalizer to free cgo memory allocations.
Go
6
star
86

focus-app

Helps you focus by hiding all your windows except the ones you are currently working in.
Objective-C
6
star
87

gopg

Go
5
star
88

isalphanumeric

A small arm64 SIMD adventure for gophers.
Go
5
star
89

dd-trace-go-demo

A simple application to show how to use dd-trace-go's tracer and profiler.
Go
5
star
90

profiler-simulator

Go
5
star
91

talks

Source and slides for my presentations.
PLpgSQL
5
star
92

node-redis-pool

A simple node.js redis pool.
JavaScript
5
star
93

countermap

Go
5
star
94

pprof-breakdown

Go
5
star
95

proftest

proftest is a C application for testing the quality of different operating system APIs for profiling.
C
5
star
96

s3.sh

Bash functions for Amazon S3. (Not complete, just scratching my itch)
Shell
5
star
97

can

Nothing to see here yet.
Go
4
star
98

js-robocom

A robocom inspired programming game for JavaScript
JavaScript
4
star
99

log

nothing to see here yet
Go
4
star
100

dd-prof-upload

Go
4
star