Import external user to firebase - go

I want to import users from an external database to firebase.
The password were hashed with a sha256 function with the password prepended by a salt (which is a UUID).
For example:
password = "123qwerty!"
salt = "cb60eb29-95a2-418e-be2a-c1c107fb1add"
hash = sha256(salt+password)
# 54ccb21d42c6961aa1b666b7cb0485f85aab2f2323399fb2959ea5e4e9f6f595
Now to import this to firebase I would do the following:
users = []*auth.UserToImport
users = append(users, (&auth.UserToImport{}).
UID("some-uid").
Email("jon.foo#example.com").
PasswordHash([]byte("54ccb21d42c6961aa1b666b7cb0485f85aab2f2323399fb2959ea5e4e9f6f595")).
PasswordSalt([]byte("cb60eb29-95a2-418e-be2a-c1c107fb1add")).
DisplayName("Jon FOO"))
h := hash.SHA256{
Rounds: 1,
InputOrder: hash.InputOrderSaltFirst,
}
res, err := cl.ImportUsers(ctx, users, auth.WithHash(h))
if err != nil {
log.Fatal(err)
}
The user is well imported in firebase (I can see it in the console), but when I try to login, I have this error The password is invalid or the user does not have a password.
I cannot see what is wrong with my way, maybe the Rounds parameter should be updated, but to what value?
Thanks!

I finally found my issue.
In my case I was giving as the PasswordHash the hex representation of the password:
PasswordHash([]byte("54ccb21d42c6961aa1b666b7cb0485f85aab2f2323399fb2959ea5e4e9f6f595")).
It turns out I have to decode first the password, like the following:
decoded, err := hex.DecodeString("54ccb21d42c6961aa1b666b7cb0485f85aab2f2323399fb2959ea5e4e9f6f595")
if err != nil {
return err
}
user := (&auth.UserToImport{}).
PasswordHash(decoded).
PasswordSalt([]byte("cb60eb29-95a2-418e-be2a-c1c107fb1add")). // the salt stays the same
...
// call ImportUsers with the same hash configuration (Rounds: 1, InputOrder: SaltFirst)
After updating this I ran the code and I could now authenticate with my imported user.
Quick note: as mentionned in the comment, the node SDK does not have the option to specify the input order (salt or password first), this seems to be an important missing feature.

Related

Failing to create JWT token when entering a custom email claim

I am trying to generate a JWT token using Go and I created the following function. I need to add the email address in jwt but as I do this I get an error saying key is of invalid type
func GenerateUserToken(expiryHours time.Duration, email string, secretKey string) (string, error) {
// Create a new token object, specifying signing method and the claims
// you would like it to contain.
token := jwt.New(jwt.SigningMethodES256)
claims := token.Claims.(jwt.MapClaims)
claims["exp"] = time.Now().Add(time.Hour * expiryHours).Unix()
claims["email"] = email
tokenStr, err := token.SignedString([]byte(secretKey))
if err != nil {
return "", err
}
return tokenStr, nil
}
What could be the reason for this? What mistake am I making?
JWT supports many signing algorithms, and that's a challenge for this particular API: depending on the signing algorithm, it expects to see a key matching that algorithm.
If you take a look at the API docs for this particular library:
https://godoc.org/github.com/dgrijalva/jwt-go
You'll see SigningMethodXXX types. These are signers selected by the signing method you pick. For ES256, it uses SigningMethodECDSA:
https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodECDSA
If you look at the Sign method docs, you'll see that it says:
For this signing method, key must be an ecdsa.PrivateKey struct
which you can parse from a PEM file using:
https://godoc.org/github.com/dgrijalva/jwt-go#ParseECPrivateKeyFromPEM
For example:
pk, err:= jwt.ParseECPrivateKeyFromPEM(pemData)
tokenStr, err := token.SignedString(pk)
This should give you a signed token with ES256.
So, you have to first figure out what kind of key you have. If you have a PEM encoding of a ECDSA key in a string, then use this method to parse it and pass the resulting private key to the signer.
If however you simply have a string secret key (like a password) and you'll share this secret key with the users of the JWT, then you can use a HMAC key. A HMAC key is simply a byte array secret that you share with your users so they can validate that the JWT was signed by you. Simply change the SigningMethod to one of the constants in:
https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodHMAC
Then, your code as it is will work with the exception that you have to change the signing method to something like jwt.New(jwt.SigningMethodHS256)

When create JWT token inside loop getting same token in jwt-go

I am creating jwt tokens using jwt-go library. Later wrote a script to load test. I have noticed when I send the many concurrent request getting same token. To check more about this I created token inside for loop and result is same.
The library that I use is https://github.com/dgrijalva/jwt-go, go version is 1.12.9.
expirationTime := time.Now().Add(time.Duration(60) * time.Minute)
for i := 1; i < 5; i++ {
claims := &jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
Issuer:"telescope",
}
_token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
var jwtKey = []byte("secret_key")
auth_token, _ := _token.SignedString(jwtKey)
fmt.Println(auth_token)
}
A JWT contains three parts: a mostly-fixed header, a set of claims, and a signature. RFC 7519 has the actual details. If the header is fixed and the claims are identical between two tokens, then the signature will be identical too, and you can easily get duplicated tokens. The two timestamp claims "iat" and "exp" are only at a second granularity, so if you issue multiple tokens with your code during the same second you will get identical results (even if you move the expirationTime calculation inside the loop).
The jwt-go library exports the StandardClaims listed in RFC 7519 ยง4.1 as a structure, which is what you're using in your code. Digging through the library code, there's nothing especially subtle here: StandardClaims uses ordinary "encoding/json" annotations, and then when a token is written out, the claims are JSON encoded and then base64-encoded. So given a fixed input, you'll get a fixed output.
If you want every token to be "different" in some way, the standard "jti" claim is a place to provide a unique ID. This isn't part of the StandardClaims, so you need to create your own custom claim type that includes it.
type UniqueClaims struct {
jwt.StandardClaims
TokenId string `json:"jti,omitempty"`
}
Then when you create the claims structure, you need to generate a unique TokenId yourself.
import (
"crypto/rand"
"encoding/base64"
)
bits := make([]byte, 12)
_, err := rand.Read(bits)
if err != nil {
panic(err)
}
claims := UniqueClaims{
StandardClaims: jwt.StandardClaims{...},
TokenId: base64.StdEncoding.EncodeToString(bits),
}
https://play.golang.org/p/zDnkamwsCi- has a complete example; every time you run it you will get a different token, even if you run it multiple times in the same second. You can base64 decode the middle part of the token by hand to see the claims, or use a tool like the https://jwt.io/ debugger to decode it.
I changed your code:
Moved calculation of expirationTime in the loop
Added 1 sec delay on each step of loop
for i := 1; i < 5; i++ {
expirationTime := time.Now().Add(time.Duration(60) * time.Minute)
claims := &jwt.StandardClaims{
ExpiresAt: expirationTime.Unix(),
Issuer: "telescope",
}
_token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
var jwtKey = []byte("secret_key")
auth_token, _ := _token.SignedString(jwtKey)
fmt.Println(auth_token)
time.Sleep(time.Duration(1) * time.Second)
}
In this case we get different tokens:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNDgsImlzcyI6InRlbGVzY29wZSJ9.G7wV-zsCYjysLEdgYAq_92JGDPsgqqOz9lZxdh5gcX8
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNDksImlzcyI6InRlbGVzY29wZSJ9.yPNV20EN3XJbGiHhe-wGTdiluJyVHXj3nIqEsfwDZ0Q
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNTAsImlzcyI6InRlbGVzY29wZSJ9.W3xFXEiVwh8xK47dZinpXFpKuvUl1LFUAiaLZZzZ2L0
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjcyNDcwNTEsImlzcyI6InRlbGVzY29wZSJ9.wYUbzdXm_VQGdFH9RynAVVouW9h6KI1tHRFJ0Y322i4
Sorry, I am not big expert in JWT and I hope somebody who is explain us this behavior from RFC point of view.
I want to get different tokens. eg : same person login in to system using different browser. so I want to keep many tokens.
It is the same user and we can get him the same token. If we want to give it another one we need to revoke previous one or the client must refresh it.

Golang Can someone explain why the compare of hash is fail

I am trying to develop a user login system, in order for that I am testing the bcrypt function of golang. But I faced some funny situation.
My bcrypt learning material is come from this, the code works well
https://medium.com/#jcox250/password-hash-salt-using-golang-b041dc94cb72
But when I wrote my own code, it keep fail in comparison.
package main
import (
"log"
"golang.org/x/crypto/bcrypt"
)
func main() {
hash1, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.MinCost)
hash2, _ := bcrypt.GenerateFromPassword([]byte("123456"), bcrypt.MinCost)
err := bcrypt.CompareHashAndPassword(hash1, hash2)
if err != nil {
log.Println(err)
} else {
log.Println("success")
}
}
Since the string for hashing is the same "123456", I except the output of my code should be success, but the outcome is crypto/bcrypt: hashedPassword is not the hash of the given password.
Can someone explain this and guide me.
The documentation for the function you are using says it compares a hash to a plaintext password - not a hash to a hash:
CompareHashAndPassword compares a bcrypt hashed password with its possible plaintext equivalent. Returns nil on success, or an error on failure.
If you were to print or compare each of the generated hashes, they would not match exactly either (that's kind of the point). But you should be able to use the CompareHashAndPassword function to check if a password was used to generate the given hash.
Try this:
err := bcrypt.CompareHashAndPassword(hash1, []byte("123456"))
if err != nil {
log.Println(err)
} else {
log.Println("success")
}

Free-IPA Ldap Intergration with GO

I am trying FreeIPA integration with golang using package "gopkg.in/ldap.v2", I created one role with name of "test" in FreeIPA UI and tried to search that role
via command line:
ldapsearch -D "cn=directory manager" -w "*****" -p 389 -h "ec2-test.eu-west-1.compute. amazonaws.com" -b "dc=ec2-test,dc=eu-west-1,dc=compute,dc=amazonaws,dc=com" -v -s sub "(&(objectclass=*)(cn=test))"
Output:
ldap_initialize( ldap://ec2-test.eu-west-1.compute.amazonaws.com:389 ) filter: (&(objectclass=*)(cn=test)) requesting: All userApplication attributes
# extended LDIF
#
# LDAPv3
# base <dc=ec2-test,dc=eu-west-1,dc=compute,dc=amazonaws,dc=com> with scope subtree
# filter: (&(objectclass=*)(cn=test))
# requesting: ALL
#
# test, roles, accounts, ec2-test.eu-west-1.compute.amazonaws.com
dn: cn=test,cn=roles,cn=accounts,dc=ec2-test,dc=eu-west-1,dc=compute,dc=amazonaws,dc=com
objectClass: groupofnames
objectClass: nestedgroup
objectClass: top
cn: test
member: uid=gow,cn=users,cn=accounts,dc=ec2-test,dc=eu-west-1,dc=comp ute,dc=amazonaws,dc=com
member: cn=trov,cn=groups,cn=accounts,dc=ec2-test,dc=eu-west-1,dc=com pute,dc=amazonaws,dc=com
# search result search: 2 result: 0 Success
# numResponses: 2
# numEntries: 1
I am trying to integrate this with my go code.
My go code is:
filterValue := "(&(objectclass="*")(cn="test"))"
searchRequest := ldap.NewSearchRequest(
baseDN, // The base dn to search
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
filterValue, // The filter to apply
[]string{"givenName", "sn", "mail", "uid", "ou", "cn", "dc", "dn"}, // A list attributes to retrieve
nil,
)
sr, err := ldap.Search(searchRequest)
if err!=nil {
fmt.Println("Error: , err)
} else {
fmt.Println("Result: , sr.Entries)
}
Unfortunately I am getting empty entries in sr.Entries
Can someone help me to get this with golang.
Note: Its working fine for users and groups.
You "probably" need to bind before you start the search using something like:
// The username and password we want to check
username := "someuser"
password := "userpassword"
bindusername := "readonly"
bindpassword := "password"
l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", "ldap.example.com", 389))
if err != nil {
log.Fatal(err)
}
defer l.Close()
// Reconnect with TLS
err = l.StartTLS(&tls.Config{InsecureSkipVerify: true})
if err != nil {
log.Fatal(err)
}
// First bind with a read only user
err = l.Bind(bindusername, bindpassword)
if err != nil {
log.Fatal(err)
}
// Search for the given username
searchRequest := ldap.NewSearchRequest(
"dc=example,dc=com",
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(objectClass=organizationalPerson)(uid=%s))", username),
[]string{"dn"},
nil,
)
Let me know how I can help.
A little late, perhaps, but if you're looking for an HTTP-based API (as you seem to do in one of the answer's comment), you could look at the following article:
http://www.admin-magazine.com/Archive/2016/34/A-REST-interface-for-FreeIPA
There is actually a very complete JSON-RPC API that is accessible via HTTP. The article linked above gives an example of how to use it. Armed with the API browser included in the FreeIPA GUI you should be able to use the HTTP client in Go to code your own functions.
Barring that GitHub has two potential libraries for you:
https://github.com/ubccr/goipa
https://github.com/tehwalris/go-freeipa
The first implements only a few functions, but these could be enough for your needs. The other one is automatically generated but I don't know how good the result is.
Note that I have used neither, so I cannot command on their usability.

Different hashes during CRAM-MD5 authentication

As an exercise, I'm trying to implement a mock SMTP server with CRAM-MD5 authentication in Go (without following RFC 2195, since it looks like it doesn't matter to the client what format the pre-hashed challenge is in; I also assume there is only one user "bob" with password "pass"). But I can't seem to get it right as the hash in response is always different from what I have on the server. I send the email using Go as such (running it as a separate package):
{...}
smtp.SendMail("localhost:25", smtp.CRAMMD5Auth("bob", "pass"),
"bob#localhost", []string{"alice#localhost"}, []byte("Hey Alice!\n"))
{...}
Here's what I do when I get the authentication acknowledgement from the client:
{...}
case strings.Contains(ms, "AUTH CRAM-MD5"):
rndbts = make([]byte, 16) // Declared at package level
b64b := make([]byte, base64.StdEncoding.EncodedLen(16))
rand.Read(rndbts)
base64.StdEncoding.Encode(b64b, rndbts)
_, err = conn.Write([]byte(fmt.Sprintf("334 %x\n", b64b)))
{...}
And this is what I do with the client's response:
{...}
{
ms = strings.TrimRight(ms, "\r\n") // The response to the challenge
ds, _ := base64.StdEncoding.DecodeString(ms)
s := strings.Split(string(ds), " ")
login := s[0] // I can get the login from the response.
h := hmac.New(md5.New, []byte("pass"))
h.Write(rndbts)
c := make([]byte, 0, ourHash.Size()) // From smtp/auth.go, not sure why we need this.
validPass := hmac.Equal(h.Sum(c), []byte(s[1]))
{...}
}
{...}
And the validPass is never true. I omitted error handling from the excerpts for brevity, but they're there in the actual code (though they're always nil). Why are the hashes different? I have looked at the source code for net/smtp, and it seems to me that I'm going in the right direction, but not quite.
I hope this helps! Runnable version is at https://play.golang.org/p/-8shx_IcLV. Also note that you'll need to fix your %x (should be %s) so you're sending the right challenge down to the client. Right now I think you're trying to hex-encode your base64 string.
Once you've fixed that, I believe this code should help you to construct the right response string on the server and compare it to what the client sent.
// Example values taken from http://susam.in/blog/auth-cram-md5/
challenge := []byte("<17893.1320679123#tesseract.susam.in>")
username := []byte("alice")
password := []byte("wonderland")
clientResponse := []byte("YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA=")
// hash the challenge with the user's password
h := hmac.New(md5.New, password)
h.Write(challenge)
hash := h.Sum(nil)
// encode the result in lowercase hexadecimal
hexEncoded := hex.EncodeToString(hash)
// prepend the username and a space
toEncode := []byte(string(username) + " " + hexEncoded)
// base64-encode the whole thing
b64Result := make([]byte, base64.StdEncoding.EncodedLen(len(toEncode)))
base64.StdEncoding.Encode(b64Result, toEncode)
// check that this is equal to what the client sent
if hmac.Equal(b64Result, clientResponse) {
fmt.Println("Matches!")
}

Resources