{"id":18983677,"url":"https://github.com/snapframework/snap-auth","last_synced_at":"2025-04-19T20:11:25.887Z","repository":{"id":1021514,"uuid":"849303","full_name":"snapframework/snap-auth","owner":"snapframework","description":"Authentication infrastructure for the Snap Framework (NOTE: This package is deprecated.  As of 0.6 this functionality is included with snap)","archived":false,"fork":false,"pushed_at":"2011-06-22T23:10:08.000Z","size":142,"stargazers_count":25,"open_issues_count":0,"forks_count":5,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-03-25T21:19:31.503Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://snapframework.com","language":"Haskell","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/snapframework.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2010-08-19T17:32:59.000Z","updated_at":"2020-03-21T07:00:29.000Z","dependencies_parsed_at":"2022-08-16T11:50:26.258Z","dependency_job_id":null,"html_url":"https://github.com/snapframework/snap-auth","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snapframework%2Fsnap-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snapframework%2Fsnap-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snapframework%2Fsnap-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/snapframework%2Fsnap-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/snapframework","download_url":"https://codeload.github.com/snapframework/snap-auth/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223713388,"owners_count":17190503,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-08T16:17:58.432Z","updated_at":"2024-11-08T16:17:58.926Z","avatar_url":"https://github.com/snapframework.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"Snap-auth provides authentication and session management functionality for\nSnap.  Eventually this will probably be moved into the snap package.  But we're\nstarting it off in a separate package until we get a better sense of how snap\ncode will be organized.\n\n\n## The Concept\n\nUser/session management has two basic levels (potentially more if you add\npermissions/roles/etc.):\n\n  - Making sure an established session between any user - authenticated or\n  \totherwise - and the server stays secure.\n  - Authenticating users, which means having proof that a user is who she says\n  \tshe is before we grant her some important priveleges in our application.\n\nThis package both of these challenges. It will likely be integrated into Snap\nas the stock solution, possibly in the 0.5 release.  \n\n\n## Session Management\n\nFirst, let's demonstrate the session management piece.\n\n### Introduction\n\nFor those familiar with Rails, the functionality is similar to \n    \n    session[:user_id] = 1234\n    session[:last_query] = \"johnpollak\"\n\nThe difference, however, is that we can't just store arbitrary data -types and\ninstead use only ByteStrings.\n\n\nWe define a type Session as\n\n    type Session = Map ByteString ByteString\n\nwhich gives us all the convenience and power of Haskell's standard Map library.\n\nIt is yet to be seen if this is effective and/or efficient in the long run but\nhas worked well so far.\n\n\n### Setting Up Your Application With Sessions\n\nLet's setup the session functionality using the CookieSession backend.\n    \n    -- Define a field to hold the session state in your application state\n    data ApplicationState = ApplicationState\n      { appSessionSt :: CookieSessionState }\n\n    -- Instantiate your app as a MonadSession\n    instance HasCookieSessionState ApplicationState where\n      getCookieSessionState = appSessionSt\n\n    -- Add some simple initializer code\n    appInit :: Initializer ApplicationState\n    appInit = do\n      cs \u003c- cookieSessionStateInitializer $ defCookieSessionState\n              { csKeyPath = \"config/site-key.txt\" \n              , csCookieName = \"myapp-session\" }\n      return $ ApplicationState cs\n\n\nAnd you are done. While you have to do this manually for now, we will in the\nfuture have the snap executable auto-generate some of this boiler plate\nfor you.\n\n\n### Usage Example\n\nLet's assume we have an odd desire to persist our user's age in our session\nstore: \n\n    import qualified Data.Map as M\n    import           Snap.Extension.CookieSession\n    \n    ...\n\n    myHandler = do\n      setInSession \"user_age\" \"32\" -- that's all we have to do!\n      render \"pages/myPage\"\n\nThe \"user_age\" field will now be available in this user's session until we\ndelete it or expire the session.\n\nWe can now retrieve it at any point with:\n    \n    myHandler2 = do\n      uage \u003c- getFromSession \"user_age\"\n      doSomethingWithUid uage\n      render \"pages/myPage2\"\n\n\n\n### Backends\n\n\n#### CookieSession\n\nThere is currently a single back-end: Snap.Extension.Session.CookieSession. It\nuses Data.Serialize to serialize the Session data type and Michael Snoyman's\nWeb.ClientSession to encrypt the cookie. The cookie is encrypted, which means\nit is fully secure and can't be read by the client/end-user.\n\nSince this method has no need for a DB back-end, it works out of the box and is\npretty much the simplest session persistence back-end to use. For those\nfamiliar, this method is the default behavior in Ruby on Rails as well.\n\nPlease see the Haddock documentation for more information.\n\n\n### Other Backends\n\nThe idea would be to add various other back-ends as desired. Redis, MongoDB,\nSQL-based databases, etc. should all be straightforward enough to implement. We\nwould just need a scheme to presist the session type in the respective\ndatabase.\n\n\n\n## Authentication\n\nThe second layer of thic package provides for user athentication. It defines an\nAuthUser datatype that holds all of the core authentication fields for\na \"user\". Let's look at it so we can get a sense for what is possible:\n\n\n    data AuthUser = AuthUser \n      { userId :: Maybe UserId\n      , userEmail :: Maybe ByteString\n      , userPassword :: Maybe Password\n      , userSalt :: Maybe ByteString\n      , userActivatedAt :: Maybe UTCTime\n      , userSuspendedAt :: Maybe UTCTime\n      , userLoginCount :: Int\n      , userFailedLoginCount :: Int\n      , userCurrentLoginAt :: Maybe UTCTime\n      , userLastLoginAt :: Maybe UTCTime\n      , userCurrentLoginIp :: Maybe ByteString\n      , userLastLoginIp :: Maybe ByteString\n      , userCreatedAt :: Maybe UTCTime\n      , userUpdatedAt :: Maybe UTCTime\n      } deriving (Read,Show,Ord,Eq)\n\n\nThe authentication piece has two key typeclasses that we need to be aware of.\n\n### MonadAuth Typeclass\n\nTo enable authentication, we need to make our application monad an instance of\nMonadAuth. While doing so, we get to choose/customize various authentication\nparameters. The simplest way to instantiate our application is simply:\n\n    instance MonadAuth Application\n\nand done. That's right, we have all the sensible defaults set up so you could\npotentially just do that. More typically, here is what you would\nspecify:\n\n    instance MonadAuth Application where\n      authAuthenticationKeys = return [\"login\", \"domain\"]\n      authUserTable = return \"myusers\"\n\nand so on. Take a look at haddocks to see what can be specified.\n\nNOTE: We are still working on implementing some of these options, but it should\nbe complete soon enough.\n\n### MonadAuthUser Typeclass\n\nNow onto the database integration. This typeclass is all about persisting users\nin some form of storage. Whatever snap database extension is being used would\nbe expected to instantiate this typeclass and have nice integration with\nMonadAuth. \n\nAs an example, Snap.Extension.DB.MongoDB has ongoing support for MonadAuth and\ninstantiates MonadAuthUser for free. See the repo at:\n\n    https://github.com/ozataman/snap-extension-mongodb\n\nA couple of key ideas to understand this typeclass are as follows:\n  \n  1. User can be looked up in 2 ways: \n      - With an internal/db-provided unique bytestring identifier. This is the\n      \t\"id\" field in most db systems.\n      - A Map of key, value pairs that can be used to look up a user in the db.\n      \tThis is the external interface and is typically submitted through a web\n      \tform. This is how the user of you application will identify herself\n      \tduring login.\n  1. The user table in the DB can contain more fields than necessary for\n     authentication. This is both natural and typical. So the saveAuthUser\n     function takes a (AuthUser, t) input. AuthUser contains the core\n     authentication fields and t is passed directly to the DB back-end to be\n     included in the save. As an example, in MongoDB implementation t is the\n     Document datatype and is merged with the AuthUser fields prior to database\n     save.\n\nAgain, this typeclass is instantiated by the DB extension you are using, so\nnormally you should not need to implement it.\n\n### Usage Example\n\nHere is a simple example. We'll provide more thorough documentation as things\ncrystallize.\n\n\n    data User = User\n      { authUser :: AuthUser\n      , myField1 :: ByteStrings\n      , myField2 :: ByteStrings\n      }\n\n    -- Construct your 'User' from the given parameters\n    -- Make sure you do validation as well - at least for now.\n    makeUser ps = return $ User { .... }\n\n    additionalUserFields :: User -\u003e Document\n    additionalUserFields u = [ \"myField1\" =: myField1 u\n                             , \"myField2\" =: myField2 u ]\n    \n    site = routes $\n      [ (\"/signup\", method GET $ newSignupH)\n      , (\"/signup\", method POST $ signupH)\n\n      , (\"/login\", method GET $ newSessionH)\n      , (\"/login\", method POST $ loginHandler \"password\" newSessionH redirHome)\n      ]\n\n    redirHome = redirect \"/\"\n\n    -- Make sure you have a 'password' field in there\n    newSessionH = render \"login\"\n\n    -- Assuming you have a signup.tpl template\n    newSignupH = render \"signup\"\n\n    -- Save user and redirect as appropriate\n    signupH :: Application ()\n    signupH = do\n      ps \u003c- getParams\n      let u = makeUser ps\n      au \u003c- saveAuthUser (u, additionalUserFields u)\n      case au of\n        Nothing -\u003e newSignupH\n        Just au' -\u003e do setSessionUserId $ userId au'\n                       redirect \"/\"\n\n\n\n## TODO/ROADMAP\n\n### Session-related\n\n#### General\n\n- Splices/handlers for easy CSRF protection token integration:\n  - csrf_meta_tag for unobtrusive JS based binding to forms (like in Rails 3)\n  - csrf_token_tag for a hidden field inside forms (in progress)\n  - verify_authenticity handler to be chained before your destructive handlers\n\n#### Planned Back-ends\n- MongoDB backend\n- HDBC-based SQL back-ends once extension-hdbc is in place\n\n#### Open Questions/Considerations\n- Possibility of using JSON-like datatype for session store.\n\n### Auth-related\n\n- Challenge/response authentication (http://pajhome.org.uk/crypt/md5/auth.html)\n  This is needed to provide secure authentication without SSL.  The goal is to\n  take as much of the burden as possible off the end user, which probably\n  means including some Javascript code for use on the client side.  If the\n  client is not javascript-enabled, then the user should have the option to\n  failover seamlessly to less secure authentication (that transmits cleartext\n  passwords across the network) or alert the user and disallow logins..  \n\n- Support for \"remember me\" and \"password reset\" tokens.\n- Perhaps make Password / Salt opaque field with a Show/Read instance for DB\n  serialization - users should really never need that stuff\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnapframework%2Fsnap-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsnapframework%2Fsnap-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsnapframework%2Fsnap-auth/lists"}