mortal-token 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2012 Jordan Hollinger
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.rdoc ADDED
@@ -0,0 +1,88 @@
1
+ == MortalToken, because some tokens shouldn't live forever
2
+
3
+ MortalToken is a library for creating tokens that self-destruct after a specified time. No need to store and look up the
4
+ token; it is self-verifying and self-expiring.
5
+
6
+ I found myself wanting simple, secure (never a good mix), and not necessarily encrypted auth tokens for some Sinatra apps.
7
+ I wanted them to expire, but didn't want to keep track of them. Not finding anything, this library emerged. The default lifespan
8
+ is two of your Earth days. This does *not* mean "48 hours from when it was created". It means that the key will be valid throughout
9
+ "today" and "tomorrow". If the lifespan were one day, a key created at 23:59 would expire at 00:00.
10
+
11
+ Steps:
12
+
13
+ 1. Generate a new token
14
+ 2. Give the client the resulting hash and salt
15
+ 3. *crickets*
16
+ 4. Receive a hash and salt from client
17
+ 5. Reconstitute the token from the salt, and test if the hashes match
18
+
19
+ Read the full documentation at {jordanhollinger.com/docs/mortal-token/}[http://jordanhollinger.com/docs/mortal-token/].
20
+
21
+ == Disclaimer
22
+
23
+ This is not intended to be the most secure thing ever, but for all I know it's less secure than I think. Suggestions and
24
+ pull requests are welcomed.
25
+
26
+ Also, it is very early and the API may go through significant changes.
27
+
28
+ == Use
29
+
30
+ Though my example is a Sinatra app, it need not be. In fact, it needn't have anything to do with the Web. It's useful
31
+ anytime you want a self-contained, self-expiring token (w/ salt).
32
+
33
+ require 'mortal-token'
34
+
35
+ # You MUST set a secret key! Otherwise, anyone who looks at the source code will be able to forge tokens.
36
+ MortalToken.secret = 'asdf092$78roasdjfjfaklmsdadASDFopijf98%2ejA#Df@sdf'
37
+
38
+ post '/login' do
39
+ if login_ok?
40
+ # Create a brand new token. Store the resulting hash and salt.
41
+ token = MortalToken.new
42
+ session[:token] = token.hash
43
+ session[:salt] = token.salt
44
+
45
+ redirect '/secret'
46
+ end
47
+ end
48
+
49
+ get '/secret' do
50
+ # Attempt to reconstitute the original token, using the salt
51
+ token = MortalToken.new(session[:salt])
52
+
53
+ # Test if the token still is (or ever was) valid
54
+ if token == session[:token]
55
+ 'Welcome!'
56
+ else
57
+ 'Go away!'
58
+ end
59
+ end
60
+
61
+ == Checking tokens
62
+
63
+ These are all valid means of checking a token's validity. In this case, they will all return true. == and === are treated the same.
64
+
65
+ token_a = MortalToken.new
66
+ token_b = MortalToken.new(token_a.salt)
67
+
68
+ token_a == token_b
69
+ token_a == token_b.to_s
70
+ token_a == token_b.hash
71
+ token_a.to_s == token_b.to_s
72
+ token_a.hash == token_b.hash
73
+ token_a.to_s == token_b.hash
74
+
75
+ == Tweak token parameters
76
+
77
+ You may tweak certain parameters of the library in order to make it more secure, less, faster, etc.
78
+ These are the defaults (see the MortalToken class for documentation about each parameter):
79
+
80
+ MortalCoil.rounds = 5
81
+ MortalCoil.valid_across = 2
82
+ MortalCoil.max_salt_length = 50
83
+ MortalCoil.min_salt_length = 10
84
+
85
+ == License
86
+ Copyright 2012 Jordan Hollinger
87
+
88
+ Licensed under the Apache License
@@ -0,0 +1,10 @@
1
+ class MortalToken
2
+ # Set defaults
3
+ self.rounds = 5
4
+ self.valid_across = 2
5
+ self.max_salt_length = 50
6
+ self.min_salt_length = 10
7
+ # Set a temporary secret key. You should set your own consistent key.
8
+ # Otherwise, existing tokens will be invalidated each time the library is loaded.
9
+ self.secret = self.salt
10
+ end
@@ -0,0 +1,76 @@
1
+ # A token hash that "self-destructs" after a certain time.
2
+ class MortalToken
3
+ # Seeds for generating random strings
4
+ RAND_SEEDS = [(0..9), ('a'..'z'), ('A'..'Z')].map(&:to_a).flatten
5
+
6
+ class << self
7
+ # The master secret token (Keep it secret! Keep it safe!). Changing this will invalidate all existing tokens.
8
+ attr_accessor :secret
9
+ # The number of encryption rounds. Defaults to 5. Changing this will invalidate existing tokens.
10
+ attr_accessor :rounds
11
+ # The number of days tokens will be valid across. Defaults to 2, which prevents a
12
+ # token generated at 23:59 from expiring at 00:00. Changing this will invalidate existing tokens.
13
+ attr_accessor :valid_across
14
+ # The maximum salt length, defaults to 50
15
+ attr_accessor :max_salt_length
16
+ # The minimum salt length, defaults to 10
17
+ attr_accessor :min_salt_length
18
+
19
+ # Returns a random string of between min_salt_length and max_salt_length alphanumeric charachters
20
+ def salt
21
+ max_length = rand(self.max_salt_length + 1)
22
+ max_length = self.min_salt_length if max_length < self.min_salt_length
23
+ pool_size = RAND_SEEDS.size
24
+ (0..max_length).map { RAND_SEEDS[rand(pool_size)] }.join('')
25
+ end
26
+ end
27
+
28
+ # The token's salt
29
+ attr_reader :salt
30
+
31
+ # To create a brand new token, do *not* pass any arguments.
32
+ #
33
+ # If you want to validate a hash from an existing token, pass
34
+ # the existing token's salt. You should usually leave "start_date" alone.
35
+ def initialize(salt=nil, start_date=nil)
36
+ @salt = salt || MortalToken.salt
37
+ @start_date = start_date || Date.today
38
+ end
39
+
40
+ # Returns the hash value of the token
41
+ def hash
42
+ val = [MortalToken.secret, salt, @start_date, end_date].join('_')
43
+ MortalToken.rounds.times { val = Digest::SHA512.hexdigest(val) }
44
+ val
45
+ end
46
+
47
+ # Alias to MortalToken::Token.hash
48
+ def to_s
49
+ hash
50
+ end
51
+
52
+ # Tests this token against another token or token hash
53
+ def ==(other_token_or_hash)
54
+ other_hash = other_token_or_hash.to_s
55
+ (earliest_date).upto(@start_date) do |date|
56
+ guess = MortalToken.new(salt, date)
57
+ return true if guess.to_s == other_hash
58
+ end
59
+ return false
60
+ end
61
+
62
+ alias_method :===, :==
63
+
64
+ private
65
+
66
+ # Returns the end date of this token
67
+ def end_date
68
+ @start_date + (MortalToken.valid_across - 1)
69
+ end
70
+
71
+ # Returns the earliest possible date this token could have been valid.
72
+ # Only useful when checking for validity.
73
+ def earliest_date
74
+ @start_date - (MortalToken.valid_across - 1)
75
+ end
76
+ end
@@ -0,0 +1,4 @@
1
+ class MortalToken
2
+ # Library version
3
+ VERSION = '0.0.1'
4
+ end
@@ -0,0 +1,5 @@
1
+ require 'date'
2
+ require 'digest/sha2'
3
+ require 'mortal-token/version'
4
+ require 'mortal-token/mortal-token'
5
+ require 'mortal-token/defaults'
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mortal-token
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Jordan Hollinger
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2012-08-02 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: A simple library for generating self-contained, self-destructing tokens
22
+ email: jordan@jordanhollinger.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - lib/mortal-token.rb
31
+ - lib/mortal-token/defaults.rb
32
+ - lib/mortal-token/mortal-token.rb
33
+ - lib/mortal-token/version.rb
34
+ - README.rdoc
35
+ - LICENSE
36
+ has_rdoc: true
37
+ homepage: http://github.com/jhollinger/mortal-token
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ segments:
51
+ - 0
52
+ version: "0"
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.3.7
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Generate self-destructing tokens
68
+ test_files: []
69
+