loadExternalStateFromUUID loads OAuth state from a flow_state record (new UUID format)
(ctx context.Context, db *storage.Connection, stateID uuid.UUID)
| 536 | |
| 537 | // loadExternalStateFromUUID loads OAuth state from a flow_state record (new UUID format) |
| 538 | func (a *API) loadExternalStateFromUUID(ctx context.Context, db *storage.Connection, stateID uuid.UUID) (context.Context, error) { |
| 539 | config := a.config |
| 540 | |
| 541 | flowState, err := models.FindFlowStateByID(db, stateID.String()) |
| 542 | if models.IsNotFoundError(err) { |
| 543 | return ctx, apierrors.NewBadRequestError(apierrors.ErrorCodeBadOAuthState, "OAuth state not found or expired") |
| 544 | } else if err != nil { |
| 545 | return ctx, apierrors.NewInternalServerError("Error loading flow state").WithInternalError(err) |
| 546 | } |
| 547 | |
| 548 | // Check expiration |
| 549 | if flowState.IsExpired(config.External.FlowStateExpiryDuration) { |
| 550 | return ctx, apierrors.NewBadRequestError(apierrors.ErrorCodeBadOAuthState, "OAuth state has expired") |
| 551 | } |
| 552 | |
| 553 | // UserID is nil at creation and set during callback, so non-nil means already consumed. |
| 554 | if flowState.IsPKCE() && flowState.UserID != nil { |
| 555 | return ctx, apierrors.NewBadRequestError(apierrors.ErrorCodeFlowStateAlreadyUsed, "State has already been used") |
| 556 | } |
| 557 | |
| 558 | ctx = withExternalProviderType(ctx, flowState.ProviderType, flowState.EmailOptional) |
| 559 | |
| 560 | if flowState.InviteToken != nil && *flowState.InviteToken != "" { |
| 561 | ctx = withInviteToken(ctx, *flowState.InviteToken) |
| 562 | } |
| 563 | if flowState.Referrer != nil && *flowState.Referrer != "" { |
| 564 | ctx = withExternalReferrer(ctx, *flowState.Referrer) |
| 565 | } |
| 566 | if flowState.OAuthClientStateID != nil { |
| 567 | ctx = withOAuthClientStateID(ctx, *flowState.OAuthClientStateID) |
| 568 | } |
| 569 | if flowState.LinkingTargetID != nil { |
| 570 | u, err := models.FindUserByID(db, *flowState.LinkingTargetID) |
| 571 | if err != nil { |
| 572 | if models.IsNotFoundError(err) { |
| 573 | return nil, apierrors.NewUnprocessableEntityError(apierrors.ErrorCodeUserNotFound, "Linking target user not found") |
| 574 | } |
| 575 | return nil, apierrors.NewInternalServerError("Database error loading user").WithInternalError(err) |
| 576 | } |
| 577 | ctx = withTargetUser(ctx, u) |
| 578 | } |
| 579 | |
| 580 | // Store the entire flow state in context for later use |
| 581 | ctx = withFlowState(ctx, flowState) |
| 582 | |
| 583 | return withSignature(ctx, stateID.String()), nil |
| 584 | } |
| 585 | |
| 586 | // Provider returns a Provider interface for the given name. |
| 587 | func (a *API) Provider(ctx context.Context, name string, scopes string) (provider.Provider, conf.OAuthProviderConfiguration, error) { |
no test coverage detected