2026-01-28 11:05:07 -06:00
|
|
|
// Copyright IBM Corp. 2020, 2025
|
2022-10-25 18:29:38 -05:00
|
|
|
// SPDX-License-Identifier: MPL-2.0
|
2023-02-02 17:15:17 -08:00
|
|
|
|
2020-08-07 12:55:02 -04:00
|
|
|
package authmethods
|
2020-07-14 14:32:47 -07:00
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2021-04-08 20:54:32 -04:00
|
|
|
"encoding/json"
|
2023-10-18 14:48:44 -07:00
|
|
|
"errors"
|
2020-07-14 14:32:47 -07:00
|
|
|
"fmt"
|
|
|
|
|
|
2021-04-08 20:54:32 -04:00
|
|
|
"github.com/hashicorp/boundary/api"
|
2023-10-18 14:48:44 -07:00
|
|
|
"github.com/hashicorp/boundary/api/authtokens"
|
2020-07-14 14:32:47 -07:00
|
|
|
)
|
|
|
|
|
|
2021-04-08 20:54:32 -04:00
|
|
|
type AuthenticateResult struct {
|
2022-10-25 18:29:38 -05:00
|
|
|
Command string `json:"-"`
|
|
|
|
|
Attributes map[string]any `json:"-"`
|
2021-04-08 20:54:32 -04:00
|
|
|
attributesRaw json.RawMessage
|
|
|
|
|
|
|
|
|
|
response *api.Response
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AuthenticateResult) MarshalJSON() ([]byte, error) {
|
|
|
|
|
// Note that a will not be nil per contract of the interface. Using a map
|
|
|
|
|
// here causes Go to sort the resulting JSON, which makes tests easier and
|
|
|
|
|
// output more predictable.
|
2022-10-25 18:29:38 -05:00
|
|
|
out := make(map[string]any)
|
2021-04-08 20:54:32 -04:00
|
|
|
if a.Command != "" {
|
|
|
|
|
out["command"] = a.Command
|
|
|
|
|
}
|
|
|
|
|
if a.Attributes != nil {
|
|
|
|
|
out["attributes"] = a.Attributes
|
|
|
|
|
}
|
|
|
|
|
return json.Marshal(out)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AuthenticateResult) UnmarshalJSON(inBytes []byte) error {
|
|
|
|
|
type in struct {
|
|
|
|
|
Command string `json:"command"`
|
|
|
|
|
Attributes json.RawMessage `json:"attributes"`
|
|
|
|
|
}
|
|
|
|
|
i := new(in)
|
|
|
|
|
if err := json.Unmarshal(inBytes, i); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
a.Command = i.Command
|
|
|
|
|
a.attributesRaw = i.Attributes
|
2022-10-25 18:29:38 -05:00
|
|
|
a.Attributes = make(map[string]any)
|
2021-04-08 20:54:32 -04:00
|
|
|
if err := json.Unmarshal(i.Attributes, &a.Attributes); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-18 14:48:44 -07:00
|
|
|
func (a AuthenticateResult) GetRawAttributes() json.RawMessage {
|
|
|
|
|
return a.attributesRaw
|
2021-04-08 20:54:32 -04:00
|
|
|
}
|
|
|
|
|
|
2023-10-18 14:48:44 -07:00
|
|
|
func (a AuthenticateResult) GetResponse() *api.Response {
|
|
|
|
|
return a.response
|
2021-04-08 20:54:32 -04:00
|
|
|
}
|
|
|
|
|
|
2023-10-18 14:48:44 -07:00
|
|
|
// GetAuthToken converts this AuthenticateResult into an AuthToken struct
|
|
|
|
|
func (a AuthenticateResult) GetAuthToken() (*authtokens.AuthToken, error) {
|
|
|
|
|
if a.attributesRaw == nil {
|
|
|
|
|
return nil, errors.New("AuthenticateResult does not have a populated raw attributes field")
|
|
|
|
|
}
|
|
|
|
|
token := new(authtokens.AuthToken)
|
|
|
|
|
if err := json.Unmarshal(a.GetRawAttributes(), token); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
return token, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Authenticate is a generic authenticate API call that returns a generic
|
|
|
|
|
// result. See the documentation for the attributes required for any given auth
|
|
|
|
|
// method.
|
|
|
|
|
//
|
|
|
|
|
// Only some auth methods support multiple commands. If the documentation does
|
|
|
|
|
// not specify what command to use when and with which attriubutes, use
|
|
|
|
|
// "login".
|
2022-10-25 18:29:38 -05:00
|
|
|
func (c *Client) Authenticate(ctx context.Context, authMethodId, command string, attributes map[string]any, opt ...Option) (*AuthenticateResult, error) {
|
2020-08-07 12:55:02 -04:00
|
|
|
if c.client == nil {
|
2020-09-24 14:34:58 -07:00
|
|
|
return nil, fmt.Errorf("nil client in Authenticate request")
|
2020-07-14 14:32:47 -07:00
|
|
|
}
|
2021-04-08 20:54:32 -04:00
|
|
|
if authMethodId == "" {
|
|
|
|
|
return nil, fmt.Errorf("empty auth method passed into Authenticate request")
|
|
|
|
|
}
|
|
|
|
|
if command == "" {
|
|
|
|
|
return nil, fmt.Errorf("empty command passed into Authenticate request")
|
|
|
|
|
}
|
2020-07-22 00:14:28 -04:00
|
|
|
|
2020-08-07 12:55:02 -04:00
|
|
|
_, apiOpts := getOpts(opt...)
|
2020-07-14 14:32:47 -07:00
|
|
|
|
2022-10-25 18:29:38 -05:00
|
|
|
reqBody := map[string]any{
|
2021-04-08 20:54:32 -04:00
|
|
|
"command": command,
|
|
|
|
|
}
|
|
|
|
|
if attributes != nil {
|
|
|
|
|
reqBody["attributes"] = attributes
|
2020-07-14 14:32:47 -07:00
|
|
|
}
|
|
|
|
|
|
2020-08-07 12:55:02 -04:00
|
|
|
req, err := c.client.NewRequest(ctx, "POST", fmt.Sprintf("auth-methods/%s:authenticate", authMethodId), reqBody, apiOpts...)
|
2020-07-14 14:32:47 -07:00
|
|
|
if err != nil {
|
2020-09-24 14:34:58 -07:00
|
|
|
return nil, fmt.Errorf("error creating Authenticate request: %w", err)
|
2020-07-14 14:32:47 -07:00
|
|
|
}
|
|
|
|
|
|
2020-08-07 12:55:02 -04:00
|
|
|
resp, err := c.client.Do(req)
|
2020-07-14 14:32:47 -07:00
|
|
|
if err != nil {
|
2020-09-24 14:34:58 -07:00
|
|
|
return nil, fmt.Errorf("error performing client request during Authenticate call: %w", err)
|
2020-07-14 14:32:47 -07:00
|
|
|
}
|
|
|
|
|
|
2021-04-08 20:54:32 -04:00
|
|
|
target := new(AuthenticateResult)
|
|
|
|
|
apiErr, err := resp.Decode(target)
|
2020-07-14 14:32:47 -07:00
|
|
|
if err != nil {
|
2020-09-24 14:34:58 -07:00
|
|
|
return nil, fmt.Errorf("error decoding Authenticate response: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if apiErr != nil {
|
|
|
|
|
return nil, apiErr
|
2020-07-14 14:32:47 -07:00
|
|
|
}
|
2021-04-08 20:54:32 -04:00
|
|
|
target.response = resp
|
2020-09-24 14:34:58 -07:00
|
|
|
return target, nil
|
2020-07-14 14:32:47 -07:00
|
|
|
}
|