go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/server/encryptedcookies/doc.go (about)

     1  // Copyright 2021 The LUCI Authors.
     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.
    14  
    15  // Package encryptedcookies implements authentication using encrypted cookies.
    16  //
    17  // A session cookie contains a session ID and a per-session encryption key.
    18  // The session cookie is itself encrypted (using an AEAD scheme) with the
    19  // server-global primary encryption key. The session ID points to a session
    20  // entity in a database that contains information about the user as well as
    21  // various OAuth2 tokens established during the login flow when the session
    22  // was created. Tokens are encrypted (again using an AEAD scheme) with the
    23  // per-session encryption key from the cookie.
    24  //
    25  // One of the stored tokens is an OAuth2 refresh token. Periodically, during the
    26  // session validation process, it is used to refresh other stored tokens
    27  // (in particular an OAuth2 access token and an OpenID Connect ID token). This
    28  // procedure fails if the user revoked access to their accounts via the ID
    29  // provider or if the user's account is not longer active (from the ID
    30  // provider's point of view). That way the state of the session is tied to the
    31  // state of the user account at the ID provider which fully offloads user
    32  // accounts management to the ID provider.
    33  //
    34  // # Configuration
    35  //
    36  // To use this module you'll need to create an encryption key and to register
    37  // an OAuth2 client with the ID provider. Instructions below assume you are
    38  // using the Google Accounts ID provider and the created encryption key will be
    39  // used as the server primary encryption key (i.e. it will be used to encrypt
    40  // not only cookies but also any other secrets that the server may wish to
    41  // encrypt).
    42  //
    43  // Start by creating two Google Secret Manager secrets (with no values) named
    44  // `tink-aead-primary` and `oauth-client-secret` in the cloud project that
    45  // the server is running in (referred to as `<cloud-project>` below). Make sure
    46  // the server account has "Secret Manager Secret Accessor" role in them. These
    47  // steps are usually done using Terraform.
    48  //
    49  // Initialize `tink-aead-primary` key by using
    50  // https://go.chromium.org/luci/server/cmd/secret-tool tool:
    51  //
    52  //	cd server/cmd/secret-tool
    53  //	go run main.go create sm://<cloud-project>/tink-aead-primary -secret-type tink-aes256-gcm
    54  //
    55  // This secret now contains a serialized Tink keyset with the primary encryption
    56  // key. If necessary it can be rotated using the same `secret-tool` tool:
    57  //
    58  //	cd server/cmd/secret-tool
    59  //	go run main.go rotation-begin sm://<cloud-project>/tink-aead-primary
    60  //	# wait several hours for the new key to propagate into all caches
    61  //	# confirm by looking at /chrome/infra/secrets/gsm/version metric
    62  //	go run main.go rotation-end sm://<cloud-project>/tink-aead-primary
    63  //
    64  // This will add a new active key to the keyset. It will be used to encrypt
    65  // new cookies, but the old key will still be recognized when decrypting
    66  // existing cookies.
    67  //
    68  // Next, create a new OAuth2 client ID that will represent your server. Follow
    69  // instructions on https://support.google.com/cloud/answer/6158849?hl=en and
    70  // pick the application type "Web application". Add an authorized redirect URI
    71  // equal to "https://<your-server-host>/auth/openid/callback". Do not add any
    72  // authorized JavaScript origins.
    73  //
    74  // After creating the OAuth2 client, note the client ID (usually looks like
    75  // "<number>-<gibberish>.apps.googleusercontent.com") and the client secret
    76  // (just a random looking string). Put the value of the secret into a new
    77  // `oauth-client-secret` Google Secret Manager secret using the `secret-tool`:
    78  //
    79  //	cd server/cmd/secret-tool
    80  //	go run main.go create sm://<cloud-project>/oauth-client-secret -secret-type password
    81  //	# paste the client secret
    82  //
    83  // All prerequisites are done. Pass the following flags to the server binary to
    84  // instruct it to use the generated secrets and the OAuth2 client:
    85  //
    86  //	server \
    87  //	    ...
    88  //	    -primary-tink-aead-key sm://tink-aead-primary \
    89  //	    -encrypted-cookies-client-id <number>-<gibberish>.apps.googleusercontent.com \
    90  //	    -encrypted-cookies-client-secret sm://oauth-client-secret \
    91  //	    -encrypted-cookies-redirect-url https://<your-server-host>/auth/openid/callback
    92  //
    93  // Note that the value of `-encrypted-cookies-redirect-url` must match exactly
    94  // what you specified when creating the OAuth2 client (e.g. if you used some
    95  // custom DNS domain name there, specify it in the `-encrypted-cookies-redirect-url`
    96  // as well).
    97  //
    98  // If you want to use a dedicated key set for encrypting cookies specifically,
    99  // replace `-primary-tink-aead-key` with `-encrypted-cookies-tink-aead-key`
   100  // (and perhaps use some different name for the secret).
   101  //
   102  // # Session store
   103  //
   104  // The module needs to know where and how to store user sessions. Link to
   105  // a concrete implementation (e.g. Cloud Datastore) by using the following
   106  // blank import line in the main.go:
   107  //
   108  //	import (
   109  //	  ...
   110  //	  // Store auth sessions in the datastore.
   111  //	  _ "go.chromium.org/luci/server/encryptedcookies/session/datastore"
   112  //	)
   113  //
   114  // # Inactive sessions cleanup
   115  //
   116  // When using Cloud Datastore as a session storage, configure a time-to-live
   117  // policy to delete `encryptedcookies.Session` entities based on `ExpireAt`
   118  // field. See https://cloud.google.com/datastore/docs/ttl. This step is usually
   119  // done via Terraform.
   120  //
   121  // A session is considered expired if it wasn't accessed for more than 14 days.
   122  //
   123  // # Exposed routes
   124  //
   125  // The module exposes 3 routes involved in the login/logout process:
   126  // `/auth/openid/login`, `/auth/openid/logout` and `/auth/openid/callback`. When
   127  // configuring your load balancer (or dispatch.yaml on Appengine), make sure
   128  // `/auth/openid/*` requests are routed appropriately.
   129  //
   130  // Note that cookies established by one server process can be validated by
   131  // another, as long as they are both configured identically (i.e. all CLI flags
   132  // mentioned above are passed to both binaries). For example, you can configure
   133  // the load balancer to pass all `/auth/openid/*` requests to a dedicated server
   134  // responsible for the login/logout, but then validate user cookies on another
   135  // server.
   136  //
   137  // Note also that the server that only validates cookies still needs write
   138  // access to the session store, to be able to refresh encrypted tokens stored
   139  // there. It means if there are some caching layers in front of the datastore,
   140  // they must be configured identically across all servers as well.
   141  //
   142  // # Running locally
   143  //
   144  // If the server is starting in the development mode (no `-prod` flag is
   145  // passed), and the `-encrypted-cookies-client-id` flag is not set, the module
   146  // switches to use fake cookies that have a similar semantics to the real
   147  // encrypted cookies, but require no extra configuration. They are absolutely
   148  // insecure and must never be used outside of local runs. They exist only to
   149  // simplify the local development of servers that use LoginURL/LogoutURL APIs.
   150  package encryptedcookies