github.com/resonatecoop/id@v1.1.0-43/oauth/client.go (about)

     1  package oauth
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"strings"
     7  
     8  	"github.com/resonatecoop/id/util"
     9  	"github.com/resonatecoop/id/util/password"
    10  	"github.com/resonatecoop/user-api/model"
    11  	"github.com/uptrace/bun"
    12  )
    13  
    14  var (
    15  	// ErrClientNotFound ...
    16  	ErrClientNotFound = errors.New("Client not found")
    17  	// ErrInvalidClientSecret ...
    18  	ErrInvalidClientSecret = errors.New("Invalid client secret")
    19  	// ErrClientIDTaken ...
    20  	ErrClientIDTaken = errors.New("Client ID taken")
    21  )
    22  
    23  // ClientExists returns true if client exists
    24  func (s *Service) ClientExists(clientID string) bool {
    25  	_, err := s.FindClientByClientID(clientID)
    26  	return err == nil
    27  }
    28  
    29  // FindClientByClientID looks up a client by client ID
    30  func (s *Service) FindClientByClientID(clientID string) (*model.Client, error) {
    31  	// Client IDs are case insensitive
    32  	ctx := context.Background()
    33  	client := new(model.Client)
    34  
    35  	err := s.db.NewSelect().
    36  		Model(client).
    37  		Where("key = LOWER(?)", clientID).
    38  		Limit(1).
    39  		Scan(ctx)
    40  
    41  	// Not Found!
    42  	if err != nil {
    43  		return nil, ErrClientNotFound
    44  	}
    45  
    46  	return client, nil
    47  }
    48  
    49  // FindClientByRedirectURI looks up a client by redirect URI
    50  func (s *Service) FindClientByApplicationURL(applicationURL string) (*model.Client, error) {
    51  	ctx := context.Background()
    52  	client := new(model.Client)
    53  
    54  	err := s.db.NewSelect().
    55  		Model(client).
    56  		Where("application_url = ? AND application_hostname IN (?)", applicationURL, bun.In(s.cnf.Origins)).
    57  		Limit(1).
    58  		Scan(ctx)
    59  
    60  	// Not Found!
    61  	if err != nil {
    62  		return nil, ErrClientNotFound
    63  	}
    64  
    65  	return client, nil
    66  }
    67  
    68  // CreateClient saves a new client to database
    69  func (s *Service) CreateClient(clientID, secret, redirectURI, applicationName, applicationHostname, applicationURL string) (*model.Client, error) {
    70  	return s.createClientCommon(s.db, clientID, secret, redirectURI, applicationName, applicationHostname, applicationURL)
    71  }
    72  
    73  // CreateClientTx saves a new client to database using injected db object
    74  func (s *Service) CreateClientTx(tx *bun.DB, clientID, secret, redirectURI, applicationName, applicationHostname, applicationURL string) (*model.Client, error) {
    75  	return s.createClientCommon(tx, clientID, secret, redirectURI, applicationName, applicationHostname, applicationURL)
    76  }
    77  
    78  // AuthClient authenticates client
    79  func (s *Service) AuthClient(clientID, secret string) (*model.Client, error) {
    80  	// Fetch the client
    81  	client, err := s.FindClientByClientID(clientID)
    82  	if err != nil {
    83  		return nil, ErrClientNotFound
    84  	}
    85  
    86  	// Verify the secret
    87  	if password.VerifyPassword(client.Secret, secret) != nil {
    88  		return nil, ErrInvalidClientSecret
    89  	}
    90  
    91  	return client, nil
    92  }
    93  
    94  func (s *Service) createClientCommon(db *bun.DB, clientID, secret, redirectURI, applicationName, applicationHostname, applicationURL string) (*model.Client, error) {
    95  	ctx := context.Background()
    96  	// Check client ID
    97  	if s.ClientExists(clientID) {
    98  		return nil, ErrClientIDTaken
    99  	}
   100  
   101  	// Hash password
   102  	secretHash, err := password.HashPassword(secret)
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  
   107  	client := &model.Client{
   108  		Key:                 strings.ToLower(clientID),
   109  		Secret:              string(secretHash),
   110  		RedirectURI:         util.StringOrNull(redirectURI),
   111  		ApplicationName:     util.StringOrNull(applicationName),
   112  		ApplicationHostname: util.StringOrNull(strings.ToLower(applicationHostname)),
   113  		ApplicationURL:      util.StringOrNull(strings.ToLower(applicationURL)),
   114  	}
   115  
   116  	_, err = s.db.NewInsert().Model(client).Exec(ctx)
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  
   121  	return client, nil
   122  }