templateVersionExternalAuthForUser returns the external auth providers referenced by the template version, with Authenticated reporting whether the given user has a usable token for each provider. Failures are returned as httperror response errors suitable for writing directly to an API response.
(ctx context.Context, templateVersion database.TemplateVersion, userID uuid.UUID)
| 353 | // as httperror response errors suitable for writing directly to an API |
| 354 | // response. |
| 355 | func (api *API) templateVersionExternalAuthForUser(ctx context.Context, templateVersion database.TemplateVersion, userID uuid.UUID) ([]codersdk.TemplateVersionExternalAuth, error) { |
| 356 | var rawProviders []database.ExternalAuthProvider |
| 357 | err := json.Unmarshal(templateVersion.ExternalAuthProviders, &rawProviders) |
| 358 | if err != nil { |
| 359 | return nil, httperror.NewResponseError(http.StatusInternalServerError, codersdk.Response{ |
| 360 | Message: "Internal error reading auth config from database", |
| 361 | Detail: err.Error(), |
| 362 | }) |
| 363 | } |
| 364 | |
| 365 | providers := make([]codersdk.TemplateVersionExternalAuth, 0) |
| 366 | for _, rawProvider := range rawProviders { |
| 367 | var config *externalauth.Config |
| 368 | for _, provider := range api.ExternalAuthConfigs { |
| 369 | if provider.ID == rawProvider.ID { |
| 370 | config = provider |
| 371 | break |
| 372 | } |
| 373 | } |
| 374 | if config == nil { |
| 375 | return nil, httperror.NewResponseError(http.StatusNotFound, codersdk.Response{ |
| 376 | Message: fmt.Sprintf("The template version references a Git auth provider %q that no longer exists.", rawProvider.ID), |
| 377 | Detail: "You'll need to update the template version to use a different provider.", |
| 378 | }) |
| 379 | } |
| 380 | |
| 381 | // This is the URL that will redirect the user with a state token. |
| 382 | redirectURL, err := api.AccessURL.Parse(fmt.Sprintf("/external-auth/%s", config.ID)) |
| 383 | if err != nil { |
| 384 | return nil, httperror.NewResponseError(http.StatusInternalServerError, codersdk.Response{ |
| 385 | Message: "Failed to parse access URL.", |
| 386 | Detail: err.Error(), |
| 387 | }) |
| 388 | } |
| 389 | |
| 390 | provider := codersdk.TemplateVersionExternalAuth{ |
| 391 | ID: config.ID, |
| 392 | Type: config.Type, |
| 393 | AuthenticateURL: redirectURL.String(), |
| 394 | DisplayName: config.DisplayName, |
| 395 | DisplayIcon: config.DisplayIcon, |
| 396 | Optional: rawProvider.Optional, |
| 397 | } |
| 398 | |
| 399 | authLink, err := api.Database.GetExternalAuthLink(ctx, database.GetExternalAuthLinkParams{ |
| 400 | ProviderID: config.ID, |
| 401 | UserID: userID, |
| 402 | }) |
| 403 | // If there isn't an auth link, then the user just isn't authenticated. |
| 404 | if errors.Is(err, sql.ErrNoRows) { |
| 405 | providers = append(providers, provider) |
| 406 | continue |
| 407 | } |
| 408 | if err != nil { |
| 409 | return nil, httperror.NewResponseError(http.StatusInternalServerError, codersdk.Response{ |
| 410 | Message: "Internal error fetching external auth link.", |
| 411 | Detail: err.Error(), |
| 412 | }) |
no test coverage detected