// shippy-user-service/handler.go ... func(srv *service)Auth(ctx context.Context, req *pb.User, res *pb.Token)error { log.Println("Logging in with:", req.Email, req.Password) user, err := srv.repo.GetByEmail(req.Email) log.Println(user) if err != nil { return err }
// Compares our given password against the hashed password // stored in the database if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password)); err != nil { return err }
// Define a secure key string used // as a salt when hashing our tokens. // Please make your own way more secure than this, // use a randomly generated md5 hash or something. key = []byte("mySuperSecretKeyLol") )
// CustomClaims is our custom metadata, which will be hashed // and sent as the second segment in our JWT type CustomClaims struct { User *pb.User jwt.StandardClaims }
// Validate the token and return the custom claims if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { return claims, nil } else { returnnil, err } }
// Encode a claim into a JWT func(srv *TokenService)Encode(user *pb.User)(string, error) {
getAll, err := client.GetAll(context.Background(), &pb.Request{}) if err != nil { log.Fatalf("Could not list users: %v", err) } for _, v := range getAll.Users { log.Println(v) }
if err != nil { log.Fatalf("Could not parse file: %v", err) }
// Create a new context which contains our given token. // This same context will be passed into both the calls we make // to our consignment-service. ctx := metadata.NewContext(context.Background(), map[string]string{ "token": token, })
// First call using our tokenised context r, err := client.CreateConsignment(ctx, consignment) if err != nil { log.Fatalf("Could not create: %v", err) } log.Printf("Created: %t", r.Created)
// Second call getAll, err := client.GetConsignments(ctx, &pb.GetRequest{}) if err != nil { log.Fatalf("Could not list consignments: %v", err) } for _, v := range getAll.Consignments { log.Println(v) } }
// shippy-consignment-service/main.go funcmain() { ... // Create a new service. Optionally include some options here. srv := micro.NewService(
// This name must match the package name given in your protobuf definition micro.Name("go.micro.srv.consignment"), micro.Version("latest"), // Our auth middleware micro.WrapHandler(AuthWrapper), ) ... }
...
// AuthWrapper is a high-order function which takes a HandlerFunc // and returns a function, which takes a context, request and response interface. // The token is extracted from the context set in our consignment-cli, that // token is then sent over to the user service to be validated. // If valid, the call is passed along to the handler. If not, // an error is returned. funcAuthWrapper(fn server.HandlerFunc)server.HandlerFunc { returnfunc(ctx context.Context, req server.Request, resp interface{})error { meta, ok := metadata.FromContext(ctx) if !ok { return errors.New("no auth meta-data found in request") }
// Note this is now uppercase (not entirely sure why this is...) token := meta["Token"] log.Println("Authenticating with token: ", token)
funcAuthInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler)(interface{}, error) {
// Set up a connection to the server. conn, err := grpc.Dial(authAddress, grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() c := pb.NewAuthClient(conn) r, err := c.ValidateToken(ctx, &pb.ValidateToken{Token: token})
if err != nil { log.Fatalf("could not authenticate: %v", err) }