Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
make "stay logged in" use a one-time token
closes #6382 Previously, the "stay logged in" cookie just used the authlogic default implementation, which is the pseudonym persistence_token. This is a problem, because that persistence_token only ever changes when the pseudonym password changes, so it's the same everywhere; so if that cookie is stolen, it's valid for a very long time. This switches us to one-time-use tokens that expire as soon as the token logs the user in once. Each user agent also gets a different one-time-use token. Change-Id: I4f20cd7759fd74590e82ed55797552e342243d49 testplan: * Check that no token is set at all when "stay logged in" isn't selected. * Check "stay logged in", and verify: * That you don't have to login again after restarting your browser, but your _normandy_session got reset. * That if you save and try to replay using the same pseudonym_credentials, they don't work the second time. * That a second browser will get a different pseudonym_credentials value, and using one token doesn't affect the other. * That once the token is used, a new one is generated and set in your cookies. Verify this new token works as well. * That logging out removes the pseudonym_credentials cookie in your browser. And also that manually restoring this cookie still doesn't log you in, since it was removed server-side as well. * Change your password, and verify that the existing "stay logged in" tokens no longer work. * Delete your pseudonym, and verify the same. Reviewed-on: https://gerrit.instructure.com/7093 Tested-by: Hudson <hudson@instructure.com> Reviewed-by: Cody Cutrer <cody@instructure.com> Reviewed-by: Zach Wily <zach@instructure.com>
- Loading branch information
1 parent
4757b59
commit 4ef50c1
Showing
9 changed files
with
298 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# | ||
# Copyright (C) 2011 Instructure, Inc. | ||
# | ||
# This file is part of Canvas. | ||
# | ||
# Canvas is free software: you can redistribute it and/or modify it under | ||
# the terms of the GNU Affero General Public License as published by the Free | ||
# Software Foundation, version 3 of the License. | ||
# | ||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY | ||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR | ||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | ||
# details. | ||
# | ||
# You should have received a copy of the GNU Affero General Public License along | ||
# with this program. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
|
||
require 'authlogic/crypto_providers/bcrypt' | ||
|
||
# A SessionPersistenceToken is a one-time-use "remember me" token to maintain a | ||
# user's login across browser sessions. It has an expiry, and it's destroyed | ||
# when the user logs out, but most importantly, it is destroyed after the first | ||
# time it is used to authenticate the user. | ||
# | ||
# The token is comprised of three fields: | ||
# <token_id:pseudonym.persistence_token:random_uuid> | ||
# | ||
# The first field allows efficient lookup, the second field verifies that the | ||
# pseudonym hasn't changed their password or anything else that changes the | ||
# persistence_token, and the last field is an unguessable token identifier | ||
# generated at random. This last field is password-equivalent, so it's stored | ||
# as a salt+hash server-side. | ||
# | ||
# much of the theory here is based on this blog post: | ||
# http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/ | ||
# | ||
# See the PseudonymSession model for the *_cookie methods that use this class. | ||
class SessionPersistenceToken < ActiveRecord::Base | ||
belongs_to :pseudonym | ||
|
||
attr_accessible :pseudonym, :crypted_token, :token_salt, :uncrypted_token | ||
attr_accessor :uncrypted_token | ||
validates_presence_of :pseudonym_id, :crypted_token, :token_salt | ||
|
||
def self.generate(pseudonym) | ||
salt = ActiveSupport::SecureRandom.hex(8) | ||
token = ActiveSupport::SecureRandom.hex(32) | ||
self.create!(:pseudonym => pseudonym, | ||
:token_salt => salt, | ||
:uncrypted_token => token, | ||
:crypted_token => self.hashed_token(salt, token)) | ||
end | ||
|
||
def self.hashed_token(salt, token) | ||
self.crypto.encrypt(salt, token) | ||
end | ||
|
||
def self.crypto | ||
Authlogic::CryptoProviders::BCrypt | ||
end | ||
|
||
def self.find_by_pseudonym_credentials(creds) | ||
token_id, persistence_token, uuid = creds.split("::") | ||
return unless token_id.present? && persistence_token.present? && uuid.present? | ||
token = self.find_by_id(token_id) | ||
return unless token | ||
return unless token.valid_token?(persistence_token, uuid) | ||
return token | ||
end | ||
|
||
def valid_token?(persistence_token, uncrypted_token) | ||
# if the pseudonym is marked deleted, the token can still be marked as | ||
# valid, but the actual login step will fail as expected. | ||
self.pseudonym && | ||
self.pseudonym.persistence_token == persistence_token && | ||
self.class.crypto.matches?(self.crypted_token, self.token_salt, uncrypted_token) | ||
end | ||
|
||
def pseudonym_credentials | ||
raise "can't build pseudonym_credentials except on just-generated token" unless uncrypted_token | ||
"#{id}::#{pseudonym.persistence_token}::#{uncrypted_token}" | ||
end | ||
|
||
def use! | ||
destroy | ||
return pseudonym | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
class AddPersistenceTokenTable < ActiveRecord::Migration | ||
def self.up | ||
create_table :session_persistence_tokens do |t| | ||
t.string :token_salt | ||
t.string :crypted_token | ||
t.integer :pseudonym_id, :limit => 8 | ||
t.timestamps | ||
end | ||
add_index :session_persistence_tokens, :pseudonym_id | ||
end | ||
|
||
def self.down | ||
drop_table :session_persistence_tokens | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters