Files
threshold/internal/auth/oidc/prompt.go
T
Elim Tsiagbey 8b8d2822df feat(oidc): OIDC Prompt (#4053)
Boundary OIDC method does not currently support passing in prompts during authentication. This change adds the capability of passing OIDC prompts. Prompts are optional OIDC parameters that determine the behaviour of the authentication server: https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest

## Changes

- New `auth_oidc_prompt` table which contains all the prompts for OIDC auth method
- New `auth_oidc_prompt_enm` table which contains possible enum values for a prompt
  - Currently supported:
    - `none`: 
The Authorization Server MUST NOT display any authentication or consent user interface pages
    - `login`: The Authorization Server SHOULD prompt the End-User for reauthentication
    - `consent`: The Authorization Server SHOULD prompt the End-User for consent before returning information to the Client
    - `select_account`: The Authorization Server SHOULD prompt the End-User to select a user account
- `oidc_auth_method_with_value_obj` view has been updated to return `prompt` value
- Add `prompt` option for OIDC auth method CLI create and update
- Pass configured prompt during OIDC authentication
- Add `prompt` API validation for create and update
2023-11-21 11:30:02 -05:00

118 lines
3.0 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package oidc
import (
"context"
"fmt"
"github.com/hashicorp/boundary/internal/auth/oidc/store"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/cap/oidc"
"google.golang.org/protobuf/proto"
)
// Prompt represents OIDC authentication prompt
type PromptParam string
const (
// Prompt values defined by OpenID specs.
// See: https://openid.net/specs/openid-connect-basic-1_0.html#RequestParameters
None PromptParam = "none"
Login PromptParam = "login"
Consent PromptParam = "consent"
SelectAccount PromptParam = "select_account"
)
var supportedPrompts = map[PromptParam]bool{
None: true,
Login: true,
Consent: true,
SelectAccount: true,
}
// SupportedPrompt returns true if the provided prompt is supported
// by boundary.
func SupportedPrompt(p PromptParam) bool {
return supportedPrompts[p]
}
// defaultPromptTableName defines the default table name for a Prompt
const defaultPromptTableName = "auth_oidc_prompt"
// Prompt defines an prompt supported by an OIDC auth method.
// It is assigned to an OIDC AuthMethod and updates/deletes to that AuthMethod
// are cascaded to its Prompts. Prompts are value objects of an AuthMethod,
// therefore there's no need for oplog metadata, since only the AuthMethod will have
// metadata because it's the root aggregate.
type Prompt struct {
*store.Prompt
tableName string
}
// NewPrompt creates a new in memory prompt assigned to an OIDC
// AuthMethod. It supports no options.
func NewPrompt(ctx context.Context, authMethodId string, p PromptParam) (*Prompt, error) {
const op = "oidc.NewPrompt"
prompt := &Prompt{
Prompt: &store.Prompt{
OidcMethodId: authMethodId,
PromptParam: string(p),
},
}
if err := prompt.validate(ctx, op); err != nil {
return nil, err // intentionally not wrapped
}
return prompt, nil
}
// validate the Prompt. On success, it will return nil.
func (s *Prompt) validate(ctx context.Context, caller errors.Op) error {
if s.OidcMethodId == "" {
return errors.New(ctx, errors.InvalidParameter, caller, "missing oidc auth method id")
}
if _, ok := supportedPrompts[PromptParam(s.PromptParam)]; !ok {
return errors.New(ctx, errors.InvalidParameter, caller, fmt.Sprintf("unsupported prompt: %s", s.Prompt))
}
return nil
}
func convertToOIDCPrompts(ctx context.Context, p []string) []oidc.Prompt {
prompts := make([]oidc.Prompt, 0, len(p))
for _, a := range p {
prompt := oidc.Prompt(a)
prompts = append(prompts, prompt)
}
return prompts
}
// AllocPrompt makes an empty one in memory
func AllocPrompt() Prompt {
return Prompt{
Prompt: &store.Prompt{},
}
}
// Clone a Prompt
func (s *Prompt) Clone() *Prompt {
cp := proto.Clone(s.Prompt)
return &Prompt{
Prompt: cp.(*store.Prompt),
}
}
// TableName returns the table name.
func (s *Prompt) TableName() string {
if s.tableName != "" {
return s.tableName
}
return defaultPromptTableName
}
// SetTableName sets the table name.
func (s *Prompt) SetTableName(n string) {
s.tableName = n
}