├── elm-package.json ├── src ├── Native │ └── Cookie.js ├── Cookie │ ├── LowLevel.elm │ └── Validate.elm └── Cookie.elm ├── LICENSE └── README.md /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.1", 3 | "summary": "Get and set browser cookies.", 4 | "repository": "http://github.com/elm-lang/cookie.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "src" 8 | ], 9 | "exposed-modules": [ 10 | "Cookie" 11 | ], 12 | "native-modules": true, 13 | "dependencies": { 14 | "elm-lang/core": "4.0.0 <= v < 5.0.0" 15 | }, 16 | "elm-version": "0.17.0 <= v < 0.18.0" 17 | } -------------------------------------------------------------------------------- /src/Native/Cookie.js: -------------------------------------------------------------------------------- 1 | 2 | var _elm_lang$cookie$Native_Cookie = function() { 3 | 4 | 5 | var get = _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { 6 | callback(_elm_lang$core$Native_Scheduler.succeed(document.cookie)); 7 | }); 8 | 9 | 10 | function set(string) 11 | { 12 | return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) 13 | { 14 | document.cookie = string; 15 | callback(_elm_lang$core$Native_Scheduler.succeed({ ctor: '_Tuple0' })); 16 | }); 17 | } 18 | 19 | return { 20 | get: get, 21 | set: set 22 | }; 23 | 24 | }(); 25 | -------------------------------------------------------------------------------- /src/Cookie/LowLevel.elm: -------------------------------------------------------------------------------- 1 | module Cookie.LowLevel exposing (get, set) 2 | {-| Low-level bindings to the JavaScript API for cookies. Generally you want 3 | to use the `Cookie` module, not `Cookie.LowLevel`. 4 | 5 | # Get and Set Cookies 6 | @docs get, set 7 | -} 8 | 9 | import Native.Cookie 10 | 11 | 12 | {-| Get the contents of `document.cookie` as a string. 13 | 14 | Generally speaking, there is no major motivation for looking at this 15 | information that is not better covered by [local-storage][local] and 16 | [session-storage][session]. 17 | 18 | [local]: http://package.elm-lang.org/packages/elm-lang/local-storage/latest 19 | [session]: http://package.elm-lang.org/packages/elm-lang/session-storage/latest 20 | -} 21 | get : Task x String 22 | get = 23 | Native.Cookie.get 24 | 25 | 26 | {-| Set a cookie using the low-level string API implemented by the browser. 27 | Instead of setting options individually, you provide a string like this: 28 | 29 | set "sessionToken=abc123;path=/;max-age=30000;secure" 30 | 31 | This gives you a safety valve in case you ever have to set a new option that 32 | is not covered by the `Cookie` module. 33 | -} 34 | set : String -> Task x () 35 | set = 36 | Native.Cookie.set -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016, Evan Czaplicki 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Evan Czaplicki nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/Cookie.elm: -------------------------------------------------------------------------------- 1 | module Cookie exposing 2 | ( set, Options, Error 3 | ) 4 | 5 | {-| Be sure to read the README first. There is some important background 6 | material there. 7 | 8 | # Example 9 | 10 | If you need to set a `userToken` cookie, you will end up writing something 11 | like this: 12 | 13 | import Cookie 14 | import Time 15 | 16 | setUserToken : String -> Task Cookie.Error () 17 | setUserToken token = 18 | Cookie.set options "userToken" token 19 | 20 | options : Cookie.Options 21 | options = 22 | { domain = Nothing 23 | , path = Nothing 24 | , maxAge = Just (7 * 24 * Time.hour) 25 | , secure = True 26 | } 27 | 28 | The important part is the `options` record. It is saying: 29 | 30 | - all subdomains of your website can see this cookie 31 | - all paths on any of those subdomains can see this cookie 32 | - the cookie will live for seven days 33 | - the cookie will only be set over HTTPS 34 | 35 | You may want to make other choices to further restrict things. 36 | 37 | # The Documentation 38 | @docs set, Options, Error 39 | 40 | -} 41 | 42 | 43 | import Cookie.LowLevel as LL 44 | 45 | 46 | {-| Setting cookies may fail for reasons including: 47 | 48 | - Some browsers only accept ASCII characters. 49 | - There is an equals sign in your key. 50 | - There are spaces or semicolons in your key or value. 51 | - You did not provide an absolute path. 52 | 53 | The `Error` type will tell you generally what kind of problem you have with 54 | a more specific error message to really pin things down. 55 | -} 56 | type Error 57 | = BadKey String 58 | | BadValue String 59 | | InvalidPath String 60 | 61 | 62 | {-| Set a key-value pair. So if you perform the following task on a page with 63 | no cookies: 64 | 65 | set "sessionToken" "abc123" 66 | 67 | The following header would be added to every request to your servers: 68 | 69 | Cookie: sessionToken=abc123 70 | 71 | As you `set` more cookies, that `Cookie` header would get more and more stuff 72 | in it. 73 | -} 74 | set : Options -> String -> String -> Task Error () 75 | set options key value = 76 | let 77 | chunks = 78 | [ key ++ "=" ++ value 79 | , format "domain" identity options.domain 80 | , format "path" identity options.path 81 | , format "max-age" toString options.maxAge 82 | , if secure then ";secure" else "" 83 | ] 84 | in 85 | LL.set (String.concat chunks) 86 | 87 | 88 | format : String -> (a -> String) -> Maybe a -> String 89 | format prefix styler option = 90 | case option of 91 | Nothing -> 92 | "" 93 | 94 | Just value -> 95 | ";" ++ prefix ++ "=" ++ styler value 96 | 97 | 98 | {-| When setting cookies, there are a few options you can tweak: 99 | 100 | The **`maxAge`** field specifies when the cookie should expire. 101 | 102 | If this is not specified, the cookie expires at the end of the session. 103 | 104 | When the **`secure`** field is true, the cookie will only be sent over secure 105 | protocols, like HTTPS. 106 | 107 | The **`domain`** field lets you restrict which domains can see your 108 | cookie. 109 | 110 | If it is not set, it defaults to the broadest domain. So if you are on 111 | `downloads.example.org` it would be set to `example.org`. This means the 112 | cookie would be available on *any* subdomain. 113 | 114 | The **`path`** field lets you restrict which pages can see your cookie. 115 | 116 | If you set this to `"/"`, the cookie will be visible on `/index.html` and 117 | `/cats/search` and any other page that starts with `/`. Similarly, if you 118 | set this to `"/cats"`, the cookie will be visible on `/cats/search` and 119 | anything else under `/cats`. 120 | 121 | If you do not set the `path`, it defaults to `"/"` allowing every page to 122 | see the cookie. If you do set the `path`, it must be an absolute path starting 123 | with `/`. 124 | 125 | -} 126 | type alias Options = 127 | { maxAge : Maybe Date 128 | , secure : Bool 129 | , domain : Maybe String 130 | , path : Maybe String 131 | } 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **Notice!** We decided not to publish this library until we found a motivating use case that was not better addressed by using the `Set-Cookie` header on an HTTP response. Please open an issue describing your particular scenario if you think you found the motivating use case! 2 | 3 | # Cookies 4 | 5 | Cookies are an ancient mistake of web browsers. They make a bunch of security problems quite easy, so consider this an expert library. 6 | 7 | If you are looking for a way to store information, you should take a look into [local-storage][local] or [session-storage][session] instead. 8 | 9 | [local]: http://package.elm-lang.org/packages/elm-lang/local-storage/latest 10 | [session]: http://package.elm-lang.org/packages/elm-lang/session-storage/latest 11 | 12 | 13 | ## What are Cookies? 14 | 15 | The crucial detail of cookies is that they all get collected and put into the `Cookies` header of every request you make. So you may have a header like this: 16 | 17 | ``` 18 | GET /spec.html HTTP/1.1 19 | Host: www.example.org 20 | Cookie: theme=light; sessionToken=abc123 21 | … 22 | ``` 23 | 24 | This means that someone set `theme` and `sessionToken` cookies. 25 | 26 | One way to set cookies is with headers on your response. For example, the server may have set the `theme` and `sessionToken` cookies by providing a response like this one: 27 | 28 | ``` 29 | HTTP/1.0 200 OK 30 | Content-type: text/html 31 | Set-Cookie: theme=light 32 | Set-Cookie: sessionToken=abc123; Expires=Wed, 09 Jun 2021 10:18:14 GMT 33 | … 34 | ``` 35 | 36 | This library provides ways to set cookies in Elm instead of with HTTP response headers. 37 | 38 | 39 | ## What is the problem? 40 | 41 | The problems break down into three major categories: security, architecture, and encoding. 42 | 43 | ### Security 44 | 45 | You may have heard of “tracking cookies” that let companies see which pages you have visited. The way it works is that a company convinces as many websites as possible to embed an “Like button” by adding a simple `