1
2
3
4
5
6
7
8
9
10
11
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
25 SCRAMSHA1 = "SCRAM-SHA-1"
26
27
28 SCRAMSHA256 = "SCRAM-SHA-256"
29 )
30
31 var (
32
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
70 type ScramAuthenticator struct {
71 mechanism string
72 source string
73 client *scram.Client
74 }
75
76 var _ SpeculativeAuthenticator = (*ScramAuthenticator)(nil)
77
78
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
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