...

Source file src/go.mongodb.org/mongo-driver/x/mongo/driver/auth/scram.go

Documentation: go.mongodb.org/mongo-driver/x/mongo/driver/auth

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  // Copyright (C) MongoDB, Inc. 2018-present.
     8  //
     9  // Licensed under the Apache License, Version 2.0 (the "License"); you may
    10  // not use this file except in compliance with the License. You may obtain
    11  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  package auth
    14  
    15  import (
    16  	"context"
    17  
    18  	"github.com/xdg-go/scram"
    19  	"github.com/xdg-go/stringprep"
    20  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    21  )
    22  
    23  const (
    24  	// SCRAMSHA1 holds the mechanism name "SCRAM-SHA-1"
    25  	SCRAMSHA1 = "SCRAM-SHA-1"
    26  
    27  	// SCRAMSHA256 holds the mechanism name "SCRAM-SHA-256"
    28  	SCRAMSHA256 = "SCRAM-SHA-256"
    29  )
    30  
    31  var (
    32  	// Additional options for the saslStart command to enable a shorter SCRAM conversation
    33  	scramStartOptions bsoncore.Document = bsoncore.BuildDocumentFromElements(nil,
    34  		bsoncore.AppendBooleanElement(nil, "skipEmptyExchange", true),
    35  	)
    36  )
    37  
    38  func newScramSHA1Authenticator(cred *Cred) (Authenticator, error) {
    39  	passdigest := mongoPasswordDigest(cred.Username, cred.Password)
    40  	client, err := scram.SHA1.NewClientUnprepped(cred.Username, passdigest, "")
    41  	if err != nil {
    42  		return nil, newAuthError("error initializing SCRAM-SHA-1 client", err)
    43  	}
    44  	client.WithMinIterations(4096)
    45  	return &ScramAuthenticator{
    46  		mechanism: SCRAMSHA1,
    47  		source:    cred.Source,
    48  		client:    client,
    49  	}, nil
    50  }
    51  
    52  func newScramSHA256Authenticator(cred *Cred) (Authenticator, error) {
    53  	passprep, err := stringprep.SASLprep.Prepare(cred.Password)
    54  	if err != nil {
    55  		return nil, newAuthError("error SASLprepping password", err)
    56  	}
    57  	client, err := scram.SHA256.NewClientUnprepped(cred.Username, passprep, "")
    58  	if err != nil {
    59  		return nil, newAuthError("error initializing SCRAM-SHA-256 client", err)
    60  	}
    61  	client.WithMinIterations(4096)
    62  	return &ScramAuthenticator{
    63  		mechanism: SCRAMSHA256,
    64  		source:    cred.Source,
    65  		client:    client,
    66  	}, nil
    67  }
    68  
    69  // ScramAuthenticator uses the SCRAM algorithm over SASL to authenticate a connection.
    70  type ScramAuthenticator struct {
    71  	mechanism string
    72  	source    string
    73  	client    *scram.Client
    74  }
    75  
    76  var _ SpeculativeAuthenticator = (*ScramAuthenticator)(nil)
    77  
    78  // Auth authenticates the provided connection by conducting a full SASL conversation.
    79  func (a *ScramAuthenticator) Auth(ctx context.Context, cfg *Config) error {
    80  	err := ConductSaslConversation(ctx, cfg, a.source, a.createSaslClient())
    81  	if err != nil {
    82  		return newAuthError("sasl conversation error", err)
    83  	}
    84  	return nil
    85  }
    86  
    87  // CreateSpeculativeConversation creates a speculative conversation for SCRAM authentication.
    88  func (a *ScramAuthenticator) CreateSpeculativeConversation() (SpeculativeConversation, error) {
    89  	return newSaslConversation(a.createSaslClient(), a.source, true), nil
    90  }
    91  
    92  func (a *ScramAuthenticator) createSaslClient() SaslClient {
    93  	return &scramSaslAdapter{
    94  		conversation: a.client.NewConversation(),
    95  		mechanism:    a.mechanism,
    96  	}
    97  }
    98  
    99  type scramSaslAdapter struct {
   100  	mechanism    string
   101  	conversation *scram.ClientConversation
   102  }
   103  
   104  var _ SaslClient = (*scramSaslAdapter)(nil)
   105  var _ ExtraOptionsSaslClient = (*scramSaslAdapter)(nil)
   106  
   107  func (a *scramSaslAdapter) Start() (string, []byte, error) {
   108  	step, err := a.conversation.Step("")
   109  	if err != nil {
   110  		return a.mechanism, nil, err
   111  	}
   112  	return a.mechanism, []byte(step), nil
   113  }
   114  
   115  func (a *scramSaslAdapter) Next(challenge []byte) ([]byte, error) {
   116  	step, err := a.conversation.Step(string(challenge))
   117  	if err != nil {
   118  		return nil, err
   119  	}
   120  	return []byte(step), nil
   121  }
   122  
   123  func (a *scramSaslAdapter) Completed() bool {
   124  	return a.conversation.Done()
   125  }
   126  
   127  func (*scramSaslAdapter) StartCommandOptions() bsoncore.Document {
   128  	return scramStartOptions
   129  }
   130  

View as plain text