1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
# OAuth 2.0
Use [OAuth 2.0][11] as the framework for Authz and extend it with OpenID Connect for
Authn.
```plaintext
Protocol Flow
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
```
* Client:
* Terraform Cloud Ember.js
* Terraform Agent
* Terraform CLI
* Agent Job
* Agent Pool
* Task Result
* Resource Owner
* User
* Team
* Authorization Server: [Cloud IDP][4]
* [RFC-6749 - OAuth 2.0][5]
The `AccessToken` and/or `IDToken` will use the [JWT][12] scheme with the some of
the standard claims. The `scope` claim will include a space delimited list of
permissions that the current subject is entitled to.
For example:
```json
{
"scope": "terraform.teams.read terraform.teams.write",
"sub": "<uuid-of-principal> | or Global ID representation of Subject. .e.g <User:1> | <Team:3> etc",
"aud": "https://app.terraform.io",
"iss": "https://idp.terraform.io",
"exp": "unix-timestamp-expires-at",
"iat": "unix-timestamp-issued-at"
}
```
If an `IDToken` is provided as part of an OpenID Connect transaction then this
token can be used to fetch any profile information update from the OpenID user
profile endpoint to stay in sync with any profile changes that occur in HCP.
## TFE Approach
<!-- In this idea, how do we prevent TFE customers from being hurt? -->
In TFE, Terraform Cloud can act as both the resource server and authorization
server (openid connect provider) and utilize [devise][6] just like it does today.
This step might be redundant but by maintaining the same authz flow for TFE as
we do in cloud we can limit the cognitive overhead of needing to remember the
differences and attempting to maintain parity.
TFE customers could plug in their own OpenID Connect server if they choose to
but would have to make sure that the standard set of "permissions" align with
the Terraform Cloud permissions.
## Coupling Assessment
<!-- In this idea, how coupled is TFC to HCP at runtime? -->
In Terraform Cloud, [cloud-idp][4] will act
as the intermediary between Terraform and HCP. All Authz permissions (claims)
for a given Principal (Resource Owner) will be included in a stateless OpenID
Connect ID Token that can be used by the Terraform Cloud API (Resource Server)
to authorize actions. Terraform Cloud will not perform runtime checks directly
against the HCP authorization server. Instead it can choose to check the
validity of a token and it's permissions by periodically checking the
disposition of the provided token against the [token introspection endpoint][1].
Request:
```plaintext
POST /introspect HTTP/1.1
Host: idp.terraform.io
Accept: application/json
Content-Type: application/json
{
"token": "mF_9.B5f-4.1JqM",
"token_type_hint": "access_token"
}
```
Response:
```
HTTP/1.1 200 OK
Content-Type: application/json
{
"active": true,
"scope": "terraform.teams.read terraform.teams.write",
"sub": "<uuid-of-principal> | or Global ID representation of Subject. .e.g <User:1> | <Team:3> etc",
"aud": "https://app.terraform.io",
"iss": "https://idp.terraform.io",
"exp": "unix-timestamp-expires-at",
"iat": "unix-timestamp-issued-at"
}
```
If when permissions/policy changes are changed in HCP this will be propagated to
the Terraform OpenID Connect server so that it can provide the current claims
for a token when the Terraform API makes a token introspection check for a given
Resource Owner.
## Expected Benefits
<!-- Not exhaustive, but what makes this compelling? -->
This allows Terraform Cloud to delegate authorization using a standard protocol
that can be replaced by other implementations that adhere to the protocols
without needing to directly couple to HCP. It will also allow the upgrade of the
Terraform Cloud token scheme to slowly align with the permissions scheme devised
by HCP. Existing Authn/Authz enforcement can remain and be upgraded gradually
until all old tokens are eventually expired.
Using OAuth 2.0 allows us to extend our existing system by introducing ways to
accept new grant types like the [SAML Assertion Grant][7], [JWT Assertion Grant][8],
[Device Authz Grant][9] etc. It allows Terraform to integrate with [other vendors][10]
via a Standards based approach that has been peer reviewed by the wider industry.
## Expected Downsides
<!-- Not exhaustive, but what are some initial concerns? -->
This path requires knowledge/education of the OAuth 2.0 protocol flow and
accepting JWT as the ID Token format for OpenID Connect Authn schemes.
This change may require hooking into how we look up the [Authenticateable Resource][2]
which could impact some of the existing [Pundit Policies][3].
## Investigation Goal
<!-- What do we need to do to understand this better? Is it feedback from others, is it a technical spike, is it a document? -->
List out the different Authz flows and model them through sequence diagrams to
understand the interface between services and how they can be extended/attacked.
[1]: https://datatracker.ietf.org/doc/html/rfc7662#section-2
[2]: https://github.com/hashicorp/atlas/blob/c060b88f91aeca9cf30b7d890445a8701f7eba82/app/models/authentication_token.rb#L96-L112
[3]: https://github.com/hashicorp/atlas/tree/c060b88f91aeca9cf30b7d890445a8701f7eba82/app/policies
[4]: https://github.com/hashicorp/cloud-idp
[5]: https://datatracker.ietf.org/doc/html/rfc6749
[6]: https://rubygems.org/gems/devise
[7]: https://datatracker.ietf.org/doc/html/rfc7522
[8]: https://datatracker.ietf.org/doc/html/rfc7523
[9]: https://datatracker.ietf.org/doc/html/rfc8628
[10]: https://openid.net/specs/openid-connect-core-1_0.html#ThirdPartyInitiatedLogin
[11]: https://www.mokhan.ca/f3a609d2c422d4d2ef1d761be4323c3955b21513f7613993acaec30dd4f76dde.html
[12]: https://datatracker.ietf.org/doc/html/rfc7519
|