github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/auth/handler/auth.go (about) 1 package handler 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "strings" 8 "sync" 9 "time" 10 11 "github.com/google/uuid" 12 13 pb "github.com/tickoalcantara12/micro/v3/proto/auth" 14 "github.com/tickoalcantara12/micro/v3/service/auth" 15 "github.com/tickoalcantara12/micro/v3/service/errors" 16 "github.com/tickoalcantara12/micro/v3/service/logger" 17 "github.com/tickoalcantara12/micro/v3/service/store" 18 authns "github.com/tickoalcantara12/micro/v3/util/auth/namespace" 19 "github.com/tickoalcantara12/micro/v3/util/auth/token" 20 "github.com/tickoalcantara12/micro/v3/util/auth/token/basic" 21 "github.com/tickoalcantara12/micro/v3/util/namespace" 22 "golang.org/x/crypto/bcrypt" 23 ) 24 25 const ( 26 storePrefixAccounts = "account" 27 storePrefixRefreshTokens = "refresh" 28 29 // used to enable login with username rather than ID (username can change e.g. email, id is stable) 30 storePrefixAccountsByName = "accountByName" 31 ) 32 33 var defaultAccount = auth.Account{ 34 ID: "admin", 35 Type: "user", 36 Scopes: []string{"admin"}, 37 Secret: "micro", 38 Metadata: map[string]string{}, 39 } 40 41 // Auth processes RPC calls 42 type Auth struct { 43 Options auth.Options 44 TokenProvider token.Provider 45 46 namespaces map[string]bool 47 sync.Mutex 48 // Prevent the generation of default accounts 49 DisableAdmin bool 50 } 51 52 // Init the auth 53 func (a *Auth) Init(opts ...auth.Option) { 54 for _, o := range opts { 55 o(&a.Options) 56 } 57 58 // setup a token provider 59 if a.TokenProvider == nil { 60 a.TokenProvider = basic.NewTokenProvider(token.WithStore(store.DefaultStore)) 61 } 62 } 63 64 func (a *Auth) setupDefaultAccount(ns string) error { 65 if ns != namespace.DefaultNamespace { 66 return nil 67 } 68 if a.DisableAdmin { 69 return nil 70 } 71 a.Lock() 72 defer a.Unlock() 73 74 // setup the namespace cache if not yet done 75 if a.namespaces == nil { 76 a.namespaces = make(map[string]bool) 77 } 78 79 // check to see if the default account has already been verified 80 if _, ok := a.namespaces[ns]; ok { 81 return nil 82 } 83 84 // check to see if we need to create the default account 85 prefix := strings.Join([]string{storePrefixAccounts, ns, ""}, joinKey) 86 recs, err := store.Read(prefix, store.ReadPrefix()) 87 if err != nil { 88 return err 89 } 90 91 hasUser := false 92 for _, rec := range recs { 93 acc := &auth.Account{} 94 err := json.Unmarshal(rec.Value, acc) 95 if err != nil { 96 return err 97 } 98 if acc.Type == "user" { 99 hasUser = true 100 break 101 } 102 } 103 104 // create the account if none exist in the namespace 105 if !hasUser { 106 acc := defaultAccount 107 acc.Issuer = ns 108 acc.Metadata["created"] = fmt.Sprintf("%d", time.Now().Unix()) 109 if err := a.createAccount(&acc); err != nil { 110 return err 111 } 112 } 113 114 // set the namespace in the cache 115 a.namespaces[ns] = true 116 return nil 117 } 118 119 // Generate an account 120 func (a *Auth) Generate(ctx context.Context, req *pb.GenerateRequest, rsp *pb.GenerateResponse) error { 121 // validate the request 122 if len(req.Id) == 0 { 123 return errors.BadRequest("auth.Auth.Generate", "ID required") 124 } 125 126 // set the defaults 127 if len(req.Type) == 0 { 128 req.Type = "user" 129 } 130 if len(req.Secret) == 0 { 131 req.Secret = uuid.New().String() 132 } 133 if req.Options == nil { 134 req.Options = &pb.Options{} 135 } 136 if len(req.Options.Namespace) == 0 { 137 req.Options.Namespace = namespace.FromContext(ctx) 138 } 139 140 // authorize the request 141 if err := authns.AuthorizeAdmin(ctx, req.Options.Namespace, "auth.Auth.Generate"); err != nil { 142 return err 143 } 144 145 // check the user does not already exists 146 key := strings.Join([]string{storePrefixAccounts, req.Options.Namespace, req.Id}, joinKey) 147 if _, err := store.Read(key); err != store.ErrNotFound { 148 return errors.BadRequest("auth", "Account with this ID already exists") 149 } 150 151 // construct the account 152 acc := &auth.Account{ 153 ID: req.Id, 154 Type: req.Type, 155 Scopes: req.Scopes, 156 Metadata: req.Metadata, 157 Issuer: req.Options.Namespace, 158 Secret: req.Secret, 159 Name: req.Name, 160 } 161 162 // create the account 163 if err := a.createAccount(acc); err != nil { 164 return err 165 } 166 167 // return the account 168 rsp.Account = serializeAccount(acc) 169 rsp.Account.Secret = req.Secret // return unhashed secret 170 return nil 171 } 172 func (a *Auth) createAccount(acc *auth.Account) error { 173 // check the user does not already exists 174 key := strings.Join([]string{storePrefixAccounts, acc.Issuer, acc.ID}, joinKey) 175 if _, err := store.Read(key); err != store.ErrNotFound { 176 return errors.BadRequest("auth.Auth.Generate", "Account with this ID already exists") 177 } 178 if acc.Metadata == nil { 179 acc.Metadata = map[string]string{ 180 "created": fmt.Sprintf("%d", time.Now().Unix()), 181 } 182 } 183 184 // set created time if not defined 185 if _, ok := acc.Metadata["created"]; !ok { 186 acc.Metadata["created"] = fmt.Sprintf("%d", time.Now().Unix()) 187 } 188 189 if acc.Name == "" { 190 acc.Name = acc.ID 191 } 192 193 usernameKey := strings.Join([]string{storePrefixAccountsByName, acc.Issuer, acc.Name}, joinKey) 194 if _, err := store.Read(usernameKey); err != store.ErrNotFound { 195 return errors.BadRequest("auth.Auth.Generate", "Account with this Name already exists") 196 } 197 198 // hash the secret 199 secret, err := hashSecret(acc.Secret) 200 if err != nil { 201 return errors.InternalServerError("auth.Auth.Generate", "Unable to hash password: %v", err) 202 } 203 acc.Secret = secret 204 205 // marshal to json 206 bytes, err := json.Marshal(acc) 207 if err != nil { 208 return errors.InternalServerError("auth.Auth.Generate", "Unable to marshal json: %v", err) 209 } 210 211 // write to the store 212 if err := store.Write(&store.Record{Key: key, Value: bytes}); err != nil { 213 return errors.InternalServerError("auth.Auth.Generate", "Unable to write account to store: %v", err) 214 } 215 216 if err := store.Write(&store.Record{Key: usernameKey, Value: bytes}); err != nil { 217 return errors.InternalServerError("auth.Auth.Generate", "Unable to write account to store: %v", err) 218 } 219 220 // set a refresh token 221 if err := a.setRefreshToken(acc.Issuer, acc.ID, uuid.New().String()); err != nil { 222 return errors.InternalServerError("auth.Auth.Generate", "Unable to set a refresh token: %v", err) 223 } 224 225 return nil 226 } 227 228 // Inspect a token and retrieve the account 229 func (a *Auth) Inspect(ctx context.Context, req *pb.InspectRequest, rsp *pb.InspectResponse) error { 230 acc, err := a.TokenProvider.Inspect(req.Token) 231 if err == token.ErrInvalidToken || err == token.ErrNotFound { 232 return errors.BadRequest("auth.Auth.Inspect", err.Error()) 233 } else if err != nil { 234 return errors.InternalServerError("auth.Auth.Inspect", "Unable to inspect token: %v", err) 235 } 236 237 rsp.Account = serializeAccount(acc) 238 return nil 239 } 240 241 // Token generation using an account ID and secret 242 func (a *Auth) Token(ctx context.Context, req *pb.TokenRequest, rsp *pb.TokenResponse) error { 243 // set defaults 244 if req.Options == nil { 245 req.Options = &pb.Options{} 246 } 247 if len(req.Options.Namespace) == 0 { 248 req.Options.Namespace = namespace.DefaultNamespace 249 } 250 251 // setup the defaults incase none exist 252 err := a.setupDefaultAccount(req.Options.Namespace) 253 if err != nil { 254 // failing gracefully here 255 logger.Errorf("Error setting up default accounts: %v", err) 256 } 257 258 // validate the request 259 if (len(req.Id) == 0 || len(req.Secret) == 0) && len(req.RefreshToken) == 0 { 260 return errors.BadRequest("auth.Auth.Token", "Credentials or a refresh token required") 261 } 262 263 // check to see if the secret is a JWT. this is a workaround to allow accounts issued 264 // by the runtime to be refreshed whilst keeping the private key in the server. 265 if a.TokenProvider.String() == "jwt" { 266 jwt := req.Secret 267 if len(req.RefreshToken) > 0 { 268 jwt = req.RefreshToken 269 } 270 271 if acc, err := a.TokenProvider.Inspect(jwt); err == nil { 272 expiry := time.Duration(int64(time.Second) * req.TokenExpiry) 273 tok, _ := a.TokenProvider.Generate(acc, token.WithExpiry(expiry)) 274 rsp.Token = serializeToken(tok, tok.Token) 275 return nil 276 } 277 } 278 279 // Declare the account id and refresh token 280 accountID := req.Id 281 refreshToken := req.RefreshToken 282 283 // If the refresh token is set, check this 284 if len(req.RefreshToken) > 0 { 285 accID, err := a.accountIDForRefreshToken(req.Options.Namespace, req.RefreshToken) 286 if err == store.ErrNotFound { 287 return errors.BadRequest("auth.Auth.Token", auth.ErrInvalidToken.Error()) 288 } else if err != nil { 289 return errors.InternalServerError("auth.Auth.Token", "Unable to lookup token: %v", err) 290 } 291 accountID = accID 292 } 293 294 acc, err := a.getAccountForID(accountID, req.Options.Namespace, "auth.Auth.Token") 295 if err != nil { 296 return err 297 } 298 299 // If the refresh token was not used, validate the secrets match and then set the refresh token 300 // so it can be returned to the user 301 if len(req.RefreshToken) == 0 { 302 if !secretsMatch(acc.Secret, req.Secret) { 303 return errors.BadRequest("auth.Auth.Token", "Secret not correct") 304 } 305 306 refreshToken, err = a.refreshTokenForAccount(req.Options.Namespace, acc.ID) 307 if err != nil { 308 return errors.InternalServerError("auth.Auth.Token", "Unable to get refresh token: %v", err) 309 } 310 } 311 312 // Generate a new access token 313 duration := time.Duration(req.TokenExpiry) * time.Second 314 tok, err := a.TokenProvider.Generate(acc, token.WithExpiry(duration)) 315 if err != nil { 316 return errors.InternalServerError("auth.Auth.Token", "Unable to generate token: %v", err) 317 } 318 319 rsp.Token = serializeToken(tok, refreshToken) 320 return nil 321 } 322 323 func (a *Auth) getAccountForID(id, namespace, errCode string) (*auth.Account, error) { 324 // Lookup the account in the store 325 key := strings.Join([]string{storePrefixAccounts, namespace, id}, joinKey) 326 recs, err := store.Read(key) 327 if err != nil && err != store.ErrNotFound { 328 return nil, errors.InternalServerError(errCode, "Unable to read from store: %v", err) 329 } 330 if err == store.ErrNotFound { 331 // maybe id is the username and not the actual ID 332 key = strings.Join([]string{storePrefixAccountsByName, namespace, id}, joinKey) 333 recs, err = store.Read(key) 334 if err == store.ErrNotFound { 335 return nil, errors.BadRequest(errCode, "Account not found with this ID") 336 } 337 if err != nil { 338 return nil, errors.InternalServerError(errCode, "Unable to read from store: %v", err) 339 } 340 } 341 342 // Unmarshal the record 343 var acc *auth.Account 344 if err := json.Unmarshal(recs[0].Value, &acc); err != nil { 345 return nil, errors.InternalServerError(errCode, "Unable to unmarshal account: %v", err) 346 } 347 return acc, nil 348 } 349 350 // set the refresh token for an account 351 func (a *Auth) setRefreshToken(ns, id, token string) error { 352 key := strings.Join([]string{storePrefixRefreshTokens, ns, id, token}, joinKey) 353 return store.Write(&store.Record{Key: key}) 354 } 355 356 // get the refresh token for an account 357 func (a *Auth) refreshTokenForAccount(ns, id string) (string, error) { 358 prefix := strings.Join([]string{storePrefixRefreshTokens, ns, id, ""}, joinKey) 359 recs, err := store.Read(prefix, store.ReadPrefix()) 360 if err != nil { 361 return "", err 362 } else if len(recs) == 0 { 363 return "", store.ErrNotFound 364 } 365 366 comps := strings.Split(recs[0].Key, "/") 367 if len(comps) != 4 { 368 return "", store.ErrNotFound 369 } 370 return comps[3], nil 371 } 372 373 // get the account ID for the given refresh token 374 func (a *Auth) accountIDForRefreshToken(ns, token string) (string, error) { 375 prefix := strings.Join([]string{storePrefixRefreshTokens, ns}, joinKey) 376 keys, err := store.List(store.ListPrefix(prefix)) 377 if err != nil { 378 return "", err 379 } 380 381 for _, k := range keys { 382 if strings.HasSuffix(k, "/"+token) { 383 comps := strings.Split(k, "/") 384 if len(comps) != 4 { 385 return "", store.ErrNotFound 386 } 387 return comps[2], nil 388 } 389 } 390 391 return "", store.ErrNotFound 392 } 393 394 func serializeToken(t *token.Token, refresh string) *pb.Token { 395 return &pb.Token{ 396 Created: t.Created.Unix(), 397 Expiry: t.Expiry.Unix(), 398 AccessToken: t.Token, 399 RefreshToken: refresh, 400 } 401 } 402 403 func hashSecret(s string) (string, error) { 404 saltedBytes := []byte(s) 405 hashedBytes, err := bcrypt.GenerateFromPassword(saltedBytes, bcrypt.DefaultCost) 406 if err != nil { 407 return "", err 408 } 409 410 hash := string(hashedBytes[:]) 411 return hash, nil 412 } 413 414 func secretsMatch(hash string, s string) bool { 415 incoming := []byte(s) 416 existing := []byte(hash) 417 return bcrypt.CompareHashAndPassword(existing, incoming) == nil 418 }