├── .github ├── workflows │ ├── LICENSE.md │ ├── update.yml │ ├── archive.yml │ ├── publish.yml │ └── ghpages.yml └── CODEOWNERS ├── LICENSE.md ├── .editorconfig ├── Makefile ├── .gitignore ├── README.md ├── CONTRIBUTING.md └── draft-hanson-oauth-cookie-response-mode.md /.github/workflows/LICENSE.md: -------------------------------------------------------------------------------- 1 | This project is in the public domain. 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | See the 4 | [guidelines for contributions](https://github.com/aaronpk/draft-hanson-cookie-response-mode/blob/main/CONTRIBUTING.md). 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # See http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*.{md,xml,org}] 6 | charset = utf-8 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Automatically generated CODEOWNERS 2 | # Regenerate with `make update-codeowners` 3 | draft-oauth-cookie-response-mode.md jared.hanson@okta.com aaron@parecki.com 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBDIR := lib 2 | include $(LIBDIR)/main.mk 3 | 4 | $(LIBDIR)/main.mk: 5 | ifneq (,$(shell grep "path *= *$(LIBDIR)" .gitmodules 2>/dev/null)) 6 | git submodule sync 7 | git submodule update $(CLONE_ARGS) --init 8 | else 9 | git clone -q --depth 10 $(CLONE_ARGS) \ 10 | -b main https://github.com/martinthomson/i-d-template $(LIBDIR) 11 | endif 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.pdf 3 | *.redxml 4 | *.swp 5 | *.txt 6 | *.upload 7 | *~ 8 | .tags 9 | /*-[0-9][0-9].xml 10 | /.gems/ 11 | /.refcache 12 | /.targets.mk 13 | /.venv/ 14 | /.vscode/ 15 | /lib 16 | /node_modules/ 17 | /versioned/ 18 | Gemfile.lock 19 | archive.json 20 | draft-oauth-cookie-response-mode.xml 21 | package-lock.json 22 | report.xml 23 | !requirements.txt 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OAuth Cookie Response Mode 2 | 3 | This is the working area for the individual Internet-Draft, "OAuth Cookie Response Mode". 4 | 5 | * [Editor's Copy](https://aaronpk.github.io/draft-hanson-oauth-cookie-response-mode/draft-hanson-oauth-cookie-response-mode.html) 6 | * [Datatracker Page](https://datatracker.ietf.org/doc/draft-hanson-oauth-cookie-response-mode) 7 | * [Individual Draft](https://datatracker.ietf.org/doc/html/draft-oauth-cookie-response-mode) 8 | 9 | 10 | ## Contributing 11 | 12 | See the 13 | [guidelines for contributions](https://github.com/aaronpk/draft-hanson-cookie-response-mode/blob/main/CONTRIBUTING.md). 14 | 15 | Contributions can be made by creating pull requests. 16 | The GitHub interface supports creating pull requests using the Edit (✏) button. 17 | 18 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This repository relates to activities in the Internet Engineering Task Force 4 | ([IETF](https://www.ietf.org/)). All material in this repository is considered 5 | Contributions to the IETF Standards Process, as defined in the intellectual 6 | property policies of IETF currently designated as 7 | [BCP 78](https://www.rfc-editor.org/info/bcp78), 8 | [BCP 79](https://www.rfc-editor.org/info/bcp79) and the 9 | [IETF Trust Legal Provisions (TLP) Relating to IETF Documents](http://trustee.ietf.org/trust-legal-provisions.html). 10 | 11 | Any edit, commit, pull request, issue, comment or other change made to this 12 | repository constitutes Contributions to the IETF Standards Process 13 | (https://www.ietf.org/). 14 | 15 | You agree to comply with all applicable IETF policies and procedures, including, 16 | BCP 78, 79, the TLP, and the TLP rules regarding code components (e.g. being 17 | subject to a Simplified BSD License) in Contributions. 18 | -------------------------------------------------------------------------------- /.github/workflows/update.yml: -------------------------------------------------------------------------------- 1 | name: "Update Generated Files" 2 | # This rule is not run automatically. 3 | # It can be run manually to update all of the files that are part 4 | # of the template, specifically: 5 | # - README.md 6 | # - CONTRIBUTING.md 7 | # - .note.xml 8 | # - .github/CODEOWNERS 9 | # - Makefile 10 | # 11 | # 12 | # This might be useful if you have: 13 | # - added, removed, or renamed drafts (including after adoption) 14 | # - added, removed, or changed draft editors 15 | # - changed the title of drafts 16 | # 17 | # Note that this removes any customizations you have made to 18 | # the affected files. 19 | on: workflow_dispatch 20 | 21 | jobs: 22 | build: 23 | name: "Update Files" 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: "Checkout" 27 | uses: actions/checkout@v2 28 | 29 | - name: "Update Generated Files" 30 | uses: martinthomson/i-d-template@v1 31 | with: 32 | make: update-files 33 | token: ${{ github.token }} 34 | 35 | - name: "Push Update" 36 | run: git push 37 | -------------------------------------------------------------------------------- /.github/workflows/archive.yml: -------------------------------------------------------------------------------- 1 | name: "Archive Issues and Pull Requests" 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * 0,2,4' 6 | repository_dispatch: 7 | types: [archive] 8 | workflow_dispatch: 9 | inputs: 10 | archive_full: 11 | description: 'Recreate the archive from scratch' 12 | default: false 13 | type: boolean 14 | 15 | jobs: 16 | build: 17 | name: "Archive Issues and Pull Requests" 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: "Checkout" 21 | uses: actions/checkout@v2 22 | 23 | # Note: No caching for this build! 24 | 25 | - name: "Update Archive" 26 | uses: martinthomson/i-d-template@v1 27 | env: 28 | ARCHIVE_FULL: ${{ inputs.archive_full }} 29 | with: 30 | make: archive 31 | token: ${{ github.token }} 32 | 33 | - name: "Update GitHub Pages" 34 | uses: martinthomson/i-d-template@v1 35 | with: 36 | make: gh-archive 37 | token: ${{ github.token }} 38 | 39 | - name: "Save Archive" 40 | uses: actions/upload-artifact@v3 41 | with: 42 | path: archive.json 43 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: "Publish New Draft Version" 2 | 3 | on: 4 | push: 5 | tags: 6 | - "draft-*" 7 | 8 | jobs: 9 | build: 10 | name: "Publish New Draft Version" 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: "Checkout" 14 | uses: actions/checkout@v3 15 | 16 | # See https://github.com/actions/checkout/issues/290 17 | - name: "Get Tag Annotations" 18 | run: git fetch -f origin ${{ github.ref }}:${{ github.ref }} 19 | 20 | - name: "Setup" 21 | id: setup 22 | run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT" 23 | 24 | - name: "Caching" 25 | uses: actions/cache@v3 26 | with: 27 | path: | 28 | .refcache 29 | .venv 30 | .gems 31 | node_modules 32 | .targets.mk 33 | key: i-d-${{ steps.setup.outputs.date }} 34 | restore-keys: i-d- 35 | 36 | - name: "Build Drafts" 37 | uses: martinthomson/i-d-template@v1 38 | with: 39 | token: ${{ github.token }} 40 | 41 | - name: "Upload to Datatracker" 42 | uses: martinthomson/i-d-template@v1 43 | with: 44 | make: upload 45 | 46 | - name: "Archive Submitted Drafts" 47 | uses: actions/upload-artifact@v3 48 | with: 49 | path: "versioned/draft-*-[0-9][0-9].*" 50 | -------------------------------------------------------------------------------- /.github/workflows/ghpages.yml: -------------------------------------------------------------------------------- 1 | name: "Update Editor's Copy" 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - README.md 7 | - CONTRIBUTING.md 8 | - LICENSE.md 9 | - .gitignore 10 | pull_request: 11 | paths-ignore: 12 | - README.md 13 | - CONTRIBUTING.md 14 | - LICENSE.md 15 | - .gitignore 16 | 17 | jobs: 18 | build: 19 | name: "Update Editor's Copy" 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: "Checkout" 23 | uses: actions/checkout@v3 24 | 25 | - name: "Setup" 26 | id: setup 27 | run: date -u "+date=%FT%T" >>"$GITHUB_OUTPUT" 28 | 29 | - name: "Caching" 30 | uses: actions/cache@v3 31 | with: 32 | path: | 33 | .refcache 34 | .venv 35 | .gems 36 | node_modules 37 | .targets.mk 38 | key: i-d-${{ steps.setup.outputs.date }} 39 | restore-keys: i-d- 40 | 41 | - name: "Build Drafts" 42 | uses: martinthomson/i-d-template@v1 43 | with: 44 | token: ${{ github.token }} 45 | 46 | - name: "Update GitHub Pages" 47 | uses: martinthomson/i-d-template@v1 48 | if: ${{ github.event_name == 'push' }} 49 | with: 50 | make: gh-pages 51 | token: ${{ github.token }} 52 | 53 | - name: "Archive Built Drafts" 54 | uses: actions/upload-artifact@v3 55 | with: 56 | path: | 57 | draft-*.html 58 | draft-*.txt 59 | -------------------------------------------------------------------------------- /draft-hanson-oauth-cookie-response-mode.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "OAuth Cookie Response Mode" 3 | abbrev: "Cookie Response Mode" 4 | category: std 5 | 6 | docname: draft-hanson-oauth-cookie-response-mode-latest 7 | submissiontype: IETF 8 | number: 9 | date: 10 | consensus: true 11 | v: 3 12 | # area: Security 13 | # workgroup: Web Authorization Protocol 14 | keyword: 15 | venue: 16 | # group: OAuth 17 | # type: Working Group 18 | # mail: oauth@example.com 19 | # arch: https://example.com/WG 20 | github: "aaronpk/draft-hanson-oauth-cookie-response-mode" 21 | latest: "https://aaronpk.github.io/draft-hanson-oauth-cookie-response-mode/draft-hanson-oauth-cookie-response-mode.html" 22 | 23 | author: 24 | - 25 | fullname: Jared Hanson 26 | organization: Okta 27 | email: jared.hanson@okta.com 28 | 29 | normative: 30 | RFC6749: 31 | RFC6265: 32 | I-D.broyer-http-cookie-auth: 33 | 34 | informative: 35 | I-D.ietf-oauth-security-topics: 36 | I-D.ietf-oauth-browser-based-apps: 37 | RFC6750: 38 | RFC7235: 39 | 40 | 41 | --- abstract 42 | 43 | This specification defines a response mode for OAuth 2.0 that uses a 44 | cookie to obtain and transmit an access token. In this mode, the access token 45 | is encoded using an HTTP Set-Cookie header and transmitted via the 46 | HTTP Cookie header to the client or resource server. 47 | 48 | 49 | --- middle 50 | 51 | # Introduction 52 | 53 | OAuth was initially created to allow third-party clients to access 54 | protected resources hosted by a resource server, with the approval of 55 | the resource owner. It has proven useful in first-party scenarios as 56 | well, where clients and resource servers are managed by the same 57 | organization. 58 | 59 | The implicit grant defined by OAuth is a flow optimized for clients 60 | implemented in a browser using a scripting language such as 61 | JavaScript. In this flow, the client is issued the access token 62 | directly, where, due to the nature of browsers, it may be exposed to 63 | other applications with access to the resource owner's user-agent. 64 | 65 | Due to this, OAuth does not support issuance of refresh tokens via 66 | the implicit grant, as refresh tokens are typically long-lasting 67 | credentials that must be kept confidential and not exposed to 68 | unauthorized parties. 69 | 70 | With the increasing adoption of OAuth, new threat models and security 71 | best current practices have been identified in 72 | {{I-D.ietf-oauth-security-topics}} and 73 | {{I-D.ietf-oauth-browser-based-apps}}. These new practices recommend 74 | the use of authorization code grant by browser-based applications and 75 | advise against use of the implicit grant. 76 | 77 | The rationale for the shift in guidance is sound, as the implicit 78 | grant is susceptible to a number of a attack vectors that aren't 79 | applicable to the authorization code grant. However, concerns around 80 | exposing tokens to unauthorized parties with access to the user-agent 81 | remain, and may be exacerbated if refresh tokens are introduced to an 82 | environment in which they were previously forbidden. 83 | 84 | These concerns are unavoidable, especially in scenarios where 85 | delegation is granted to third-party clients. However, first-party 86 | scenarios have the ability to use cookies, which can be limited in 87 | such a way that they aren't exposed to JavaScript. Such limits 88 | mitigate various attack vectors, benefiting scenarios in which use of 89 | cookies is applicable. 90 | 91 | This specification defines a response mode for OAuth 2.0 that uses a 92 | cookie to transmit an access token. In this mode, the access token 93 | is encoded using an HTTP `Set-Cookie` header and transmitted via the 94 | the HTTP `Cookie` header to the client or resource server. 95 | 96 | # Conventions and Definitions 97 | 98 | {::boilerplate bcp14-tagged} 99 | 100 | # User-Agent-based Applications 101 | 102 | This specification applies to user-agent-based applications, which is 103 | a client profile defined in Section 2.1 of {{RFC6749}}. A user-agent- 104 | based application is a public client in which the client code is 105 | downloaded from a web server and executes within a user-agent (e.g., 106 | web browser) on the device used by the resource owner. Protocol data 107 | and credentials are easily accessible (and often visible) to the 108 | resource owner. Since such applications reside within the user- 109 | agent, they can make seamless use of the user-agent capabilities when 110 | requesting authorization. 111 | 112 | This specification has been designed around the following user-agent- 113 | based application profiles, representing common architectual patterns 114 | for building web applications: 115 | 116 | "multi-page application": 117 | : A multi-page application (MPA) is a user- 118 | agent-based application that interacts with the user using 119 | hypertext, where each interaction triggers a request to a server 120 | which responds with a new page that is loaded into the browser. A 121 | MPA makes use of HTML links, forms, and HTTP redirects. 122 | 123 | "single-page application": 124 | : A single-page application (SPA) is a user- 125 | agent-based application that interacts with the user by 126 | dynamically rewriting the current page rather than loading entire 127 | new pages from a server. A SPA makes use of JavaScript and web 128 | browser APIs. 129 | 130 | "hybrid application": 131 | : A hybrid application is a user-agent-based 132 | application that interacts with the user using both hypertext and 133 | dynamic scripting. A hybrid application makes use of both HTML 134 | and/or JavaScript within a single page and the entirety of the 135 | application may span multiple pages. 136 | 137 | # Cookie Response Mode 138 | 139 | This specification defines the Cookie Response Mode, which is 140 | described with its `response_mode` parameter value: 141 | 142 | "cookie": 143 | : In this mode, the access token parameter of an authorization 144 | response is encoded in a Set-Cookie HTTP header when responding to 145 | the client. 146 | 147 | 148 | ## Authorization Request 149 | 150 | The client constructs the request URI as defined in Section 4.2.1 of 151 | {{RFC6749}}, and includes the response_mode extension parameter as 152 | defined by this specification. 153 | 154 | The client directs the resource owner to the constructed URI using an 155 | HTTP redirection response, or by other means available to it via the 156 | user-agent. 157 | 158 | For example, the client directs the user-agent to make the following 159 | HTTP request using TLS (with extra line breaks for display purposes 160 | only): 161 | 162 | GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz 163 | &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb 164 | &response_mode=cookie HTTP/1.1 165 | Host: server.example.com 166 | 167 | ## Access Token Response 168 | 169 | If the resource owner grants the access request, the authorization 170 | server issues an access token and delivers it to the client by 171 | including an HTTP Set-Cookie header in the redirection response to 172 | the client as defined by [RFC6265]. Any additional parameters other 173 | than the access token are added to the fragment component of the 174 | redirection URI as defined in Section 4.2.2 of [RFC6749]. 175 | 176 | For example, the authorization server redirects the user-agent by 177 | sending the following HTTP response (with extra line breaks for 178 | display purposes only): 179 | 180 | HTTP/1.1 302 Found 181 | Location: http://example.com/cb#state=xyz&token_type=example 182 | &expires_in=3600 183 | Set-Cookie: at=2YotnFZFEjr1zCsicMWpAA; Path=/ 184 | 185 | # Accessing Protected Resources 186 | 187 | This specification describes how to use the HTTP state management 188 | mechanism defined by {{RFC6265}} to access protected resources. 189 | 190 | ## Bearer Token Usage 191 | 192 | {{RFC6750}} defines how to use bearer tokens in HTTP requests, 193 | primarily using the WWW-Authenticate and Authorization HTTP headers 194 | defined by {{RFC7235}}. 195 | 196 | Many user-agent-based applications, particularly multi-page 197 | applications, do not make use of the HTTP Authentication framework to 198 | authorize access to protected resources. Instead, these applications 199 | use cookies to establish a "session" for subsequent requests to the 200 | server. This section defines a method of sending bearer access 201 | tokens in resource requests to resource servers that makes use of the 202 | HTTP state management mechanism implemented by the user-agent within 203 | which a user-agent-based application is executing. 204 | 205 | ### Cookie Request Header Field 206 | 207 | When sending the access token using the HTTP state management 208 | mechanism, the client uses the "Cookie" request header field to 209 | transmit the access token. 210 | 211 | For example: 212 | 213 | GET /resource HTTP/1.1 214 | Host: server.example.com 215 | Cookie: at=2YotnFZFEjr1zCsicMWpAA 216 | Accept: text/html 217 | 218 | This method is implemented by user-agents in such a way that the 219 | "Cookie" request header field, and associated access token, are are 220 | automatically presented by the client to the resource server, 221 | typically without any involvement from the client developer. 222 | 223 | For example, a multi-page application would make the above request 224 | when the end-user clicks on a link: 225 | 226 | Resource 227 | 228 | The corresponding HTTP POST request to the same endpoint would be 229 | made when the end-user submits a form, for example: 230 | 231 |
232 | 233 | 234 | 235 |
236 | 237 | ## Unauthenticated Requests 238 | 239 | If the protected resource request does not include authentication 240 | credentials or does not contain an access token that enables access 241 | to the protected resource, the resource server MAY include the HTTP 242 | `WWW-Authenticate` response header field. 243 | 244 | If the resource server includes the HTTP `WWW-Authenticate` response 245 | header field, it SHOULD use the auth-scheme value `Cookie` as defined 246 | by {{I-D.broyer-http-cookie-auth}}. 247 | 248 | For example: 249 | 250 | HTTP/1.1 401 Unauthorized 251 | WWW-Authenticate: Cookie realm="example" 252 | form-action="/login" 253 | cookie-name=at 254 | Content-Type: text/html 255 | 256 | Unauthorized 257 |
258 | 259 | 260 | 261 | 262 | 263 |
264 | 265 | # TODO 266 | 267 | Discuss cookie attributes like Expires and Path in relation to 268 | Resource servers and token expiration times. 269 | 270 | Works when authorization server and the resource server (and the 271 | client?) are the same entity. - Client may need to be same entity as 272 | AS, depending on browser cookie restrictinos like ITP. 273 | 274 | 275 | # Security Considerations 276 | 277 | TODO Security 278 | 279 | 280 | # IANA Considerations 281 | 282 | This document has no IANA actions. 283 | 284 | 285 | --- back 286 | 287 | # Acknowledgments 288 | {:numbered="false"} 289 | 290 | TODO acknowledge. 291 | --------------------------------------------------------------------------------