FromRequest returns the signed token from the request, if it exists and is valid. The caller must check that the token matches the request.
(r *http.Request, mgr cryptokeys.SigningKeycache)
| 76 | // FromRequest returns the signed token from the request, if it exists and is |
| 77 | // valid. The caller must check that the token matches the request. |
| 78 | func FromRequest(r *http.Request, mgr cryptokeys.SigningKeycache) (*SignedToken, bool) { |
| 79 | // Get all signed app tokens from the request. This includes the query |
| 80 | // parameter and all matching cookies sent with the request. If there are |
| 81 | // somehow multiple signed app token cookies, we want to try all of them |
| 82 | // (up to 4). The first one that is valid is used. |
| 83 | // |
| 84 | // Browsers will send all cookies in the request, even if there are multiple |
| 85 | // with the same name on different paths. |
| 86 | // |
| 87 | // If using a query parameter the request MUST be a terminal request. We use |
| 88 | // this to support cross-domain terminal access for the web terminal. |
| 89 | var ( |
| 90 | tokens = []string{} |
| 91 | hasQueryParam = false |
| 92 | ) |
| 93 | if q := r.URL.Query().Get(codersdk.SignedAppTokenQueryParameter); q != "" { |
| 94 | hasQueryParam = true |
| 95 | tokens = append(tokens, q) |
| 96 | } |
| 97 | for _, cookie := range r.Cookies() { |
| 98 | if cookie.Name == codersdk.SignedAppTokenCookie { |
| 99 | tokens = append(tokens, cookie.Value) |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | if len(tokens) > 4 { |
| 104 | tokens = tokens[:4] |
| 105 | } |
| 106 | |
| 107 | ctx := r.Context() |
| 108 | for _, tokenStr := range tokens { |
| 109 | var token SignedToken |
| 110 | err := jwtutils.Verify(ctx, mgr, tokenStr, &token, jwtutils.WithVerifyExpected(jwt.Expected{ |
| 111 | Time: time.Now(), |
| 112 | })) |
| 113 | if err == nil { |
| 114 | req := token.Request.Normalize() |
| 115 | if hasQueryParam && req.AccessMethod != AccessMethodTerminal { |
| 116 | // The request must be a terminal request if we're using a |
| 117 | // query parameter. |
| 118 | return nil, false |
| 119 | } |
| 120 | |
| 121 | err := req.Check() |
| 122 | if err == nil { |
| 123 | // The request has a valid signed app token, which is a valid |
| 124 | // token signed by us. The caller must check that it matches |
| 125 | // the request. |
| 126 | return &token, true |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | return nil, false |
| 132 | } |