(t *testing.T)
| 152 | } |
| 153 | |
| 154 | func TestOAuth2ProviderTokenExchange(t *testing.T) { |
| 155 | t.Parallel() |
| 156 | |
| 157 | db, pubsub := dbtestutil.NewDB(t) |
| 158 | ownerClient := coderdtest.New(t, &coderdtest.Options{ |
| 159 | Database: db, |
| 160 | Pubsub: pubsub, |
| 161 | }) |
| 162 | owner := coderdtest.CreateFirstUser(t, ownerClient) |
| 163 | ctx := testutil.Context(t, testutil.WaitLong) |
| 164 | apps := generateApps(ctx, t, ownerClient, "token-exchange") |
| 165 | |
| 166 | //nolint:gocritic // OAauth2 app management requires owner permission. |
| 167 | secret, err := ownerClient.PostOAuth2ProviderAppSecret(ctx, apps.Default.ID) |
| 168 | require.NoError(t, err) |
| 169 | |
| 170 | // The typical oauth2 flow from this point is: |
| 171 | // Create an oauth2.Config using the id, secret, endpoints, and redirect: |
| 172 | // cfg := oauth2.Config{ ... } |
| 173 | // Display url for the user to click: |
| 174 | // userClickURL := cfg.AuthCodeURL("random_state") |
| 175 | // userClickURL looks like: https://idp url/authorize? |
| 176 | // client_id=... |
| 177 | // response_type=code |
| 178 | // redirect_uri=.. (back to backstage url) .. |
| 179 | // scope=... |
| 180 | // state=... |
| 181 | // *1* User clicks "Allow" on provided page above |
| 182 | // The redirect_uri is followed which sends back to backstage with the code and state |
| 183 | // Now backstage has the info to do a cfg.Exchange() in the back to get an access token. |
| 184 | // |
| 185 | // ---NOTE---: If the user has already approved this oauth app, then *1* is optional. |
| 186 | // Coder can just immediately redirect back to backstage without user intervention. |
| 187 | tests := []struct { |
| 188 | name string |
| 189 | app codersdk.OAuth2ProviderApp |
| 190 | // The flow is setup(ctx, client, user) -> preAuth(cfg) -> cfg.AuthCodeURL() -> preToken(cfg) -> cfg.Exchange() |
| 191 | setup func(context.Context, *codersdk.Client, codersdk.User) error |
| 192 | preAuth func(valid *oauth2.Config) |
| 193 | authError string |
| 194 | preToken func(valid *oauth2.Config) |
| 195 | tokenError string |
| 196 | |
| 197 | // If null, assume the code should be valid. |
| 198 | defaultCode *string |
| 199 | // custom allows some more advanced manipulation of the oauth2 exchange. |
| 200 | exchangeMutate []oauth2.AuthCodeOption |
| 201 | }{ |
| 202 | { |
| 203 | name: "AuthInParams", |
| 204 | app: apps.Default, |
| 205 | preAuth: func(valid *oauth2.Config) { |
| 206 | valid.Endpoint.AuthStyle = oauth2.AuthStyleInParams |
| 207 | }, |
| 208 | }, |
| 209 | { |
| 210 | name: "AuthInvalidAppID", |
| 211 | app: apps.Default, |
nothing calls this directly
no test coverage detected