github.com/System-Glitch/goyave/v2@v2.10.3-0.20200819142921-51011e75d504/docs_src/src/guide/advanced/authentication.md (about)

     1  ---
     2  meta:
     3    - name: "og:title"
     4      content: "Authentication - Goyave"
     5    - name: "twitter:title"
     6      content: "Authentication - Goyave"
     7    - name: "title"
     8      content: "Authentication - Goyave"
     9  ---
    10  
    11  # Authentication <Badge text="Since v2.5.0"/>
    12  
    13  [[toc]]
    14  
    15  ## Introduction
    16  
    17  Goyave provides a convenient and expandable way of handling authentication in your application. Authentication can be enabled when registering your routes:
    18  
    19  ``` go
    20  import "github.com/System-Glitch/goyave/v2/auth"
    21  
    22  //...
    23  
    24  authenticator := auth.Middleware(&model.User{}, &auth.BasicAuthenticator{})
    25  router.Middleware(authenticator)
    26  ```
    27  
    28  Authentication is handled by a simple middleware calling an **Authenticator**. This middleware also needs a model, which will be used to fetch user information on a successful login.
    29  
    30  #### auth.Middleware
    31  
    32  Middleware create a new authenticator middleware to authenticate the given model using the given authenticator.
    33  
    34  | Parameters                    | Return              |
    35  |-------------------------------|---------------------|
    36  | `model interface{}`           | `goyave.Middleware` |
    37  | `authenticator Authenticator` |                     |
    38  
    39  **Example:**
    40  ``` go
    41  authenticator := auth.Middleware(&model.User{}, &auth.BasicAuthenticator{})
    42  router.Middleware(authenticator)
    43  ```
    44  
    45  ## Authenticators
    46  
    47  This section will go into more details about Authenticators and explain the built-in ones. You will also learn how to implement an authenticator yourself.
    48  
    49  **`Authenticator`** is a functional interface with a single method accepting a request and a model pointer as parameters.
    50  
    51  ``` go
    52  Authenticate(request *goyave.Request, user interface{}) error
    53  ```
    54  
    55  The goal of this function is to check user credentials, most of the time from the request's **headers**. If they are correct and the user can be authenticated, the `user` parameter is updated with the user's information. User information is most of the time fetched from the database.
    56  
    57  On the other hand, if the user cannot be authenticated, the `Authenticate` method must return an `error` containing a localized message. For example, the error could be that the token lifetime is expired, thus "Your authentication token is expired." will be returned.
    58  
    59  Authenticators use their model's struct fields tags to know which field to use for username and password. To make your model compatible with authentication, you must add the `auth:"username"` and `auth:"password"` tags:
    60  
    61  ``` go
    62  type User struct {
    63  	gorm.Model
    64  	Email    string `gorm:"type:varchar(100);unique_index" auth:"username"`
    65  	Name     string `gorm:"type:varchar(100)"`
    66  	Password string `gorm:"type:varchar(60)" auth:"password"`
    67  }
    68  ```
    69  
    70  ::: warning
    71  - The username should be **unique**.
    72  - Passwords should be **hashed** before being stored in the database.
    73  
    74  Built-in Goyave Authenticators use [`bcrypt`](https://pkg.go.dev/golang.org/x/crypto/bcrypt) to check if a password matches the user request.
    75  :::
    76  
    77  When a user is successfully authenticated on a protected route, its information is available in the controller handler, through the request `User` field.
    78  
    79  ``` go
    80  func Hello(response *goyave.Response, request *goyave.Request) {
    81  	user := request.User.(*model.User)
    82  	response.String(http.StatusOK, "Hello " + user.Name)
    83  }
    84  ```
    85  
    86  ::: tip
    87  Remember that Goyave is primarily focused on APIs. It doesn't use session nor cookies in its core features, making requests **stateless**.
    88  
    89  If you want to implement cookie or session-based authentication, be sure to protect your application from [CSRF attacks](https://en.wikipedia.org/wiki/Cross-site_request_forgery).
    90  :::
    91  
    92  ### Basic Auth
    93  
    94  [Basic authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) is an authentication method using the `Authorization` header and a simple username and password combination with the following format: `username:password`, encoded in base64. There are two built-in Authenticators for Basic auth.
    95  
    96  #### Database provider
    97  
    98  This Authenticator fetches the user information from the database, using the field tags explained earlier.
    99  
   100  To apply this protection to your routes, add the following middleware:
   101  
   102  ``` go
   103  authenticator := auth.Middleware(&model.User{}, &auth.BasicAuthenticator{})
   104  router.Middleware(authenticator)
   105  ```
   106  
   107  You can then try requesting a protected route:
   108  ```
   109  $ curl -u username:password http://localhost:8080/hello
   110  Hello Jérémy
   111  ```
   112  
   113  #### Config provider
   114  
   115  This Authenticator fetches the user information from the config. This method is good for quick proof-of-concepts, as it requires minimum setup, but shouldn't be used in real-world applications.
   116  
   117  - The `auth.basic.username` config entry defines the username that must be matched.
   118  - The `auth.basic.password` config entry defines the password that must be matched.
   119  
   120  To apply this protection to your routes, start by adding the following content to your configuration:
   121  
   122  ```json
   123  {
   124    ...
   125    "auth": {
   126      "basic": {
   127        "username": "admin",
   128        "password": "admin"
   129      }
   130    }
   131  }
   132  ```
   133  
   134  Then, add the following middleware:
   135  
   136  ``` go
   137  router.Middleware(auth.ConfigBasicAuth())
   138  ```
   139  
   140  The model used for this Authenticator is `auth.BasicUser`:
   141  ``` go
   142  type BasicUser struct {
   143  	Name string
   144  }
   145  ```
   146  
   147  You can then try requesting a protected route:
   148  ```
   149  $ curl -u username:password http://localhost:8080/hello
   150  ```
   151  
   152  #### auth.ConfigBasicAuth
   153  
   154  Create a new authenticator middleware for config-based Basic authentication. On auth success, the request user is set to a `auth.BasicUser`.
   155  The user is authenticated if the `auth.basic.username` and `auth.basic.password` config entries match the request's Authorization header.
   156  
   157  | Parameters | Return              |
   158  |------------|---------------------|
   159  |            | `goyave.Middleware` |
   160  
   161  ### JSON Web Token (JWT)
   162  
   163  JWT, or [JSON Web Token](https://en.wikipedia.org/wiki/JSON_Web_Token), is an open standard of authentication that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA. Goyave uses HMAC-SHA256 in its implementation.
   164  
   165  JTW Authentication comes with two configuration entries:
   166  
   167  - `auth.jwt.expiry`: the number of seconds a token is valid for. Defaults to `300` (5 minutes).
   168  - `auth.jwt.secret`: the secret used for the HMAC signature. This entry **doesn't have a default value**, you need to define it yourself. Use a key that is **at least 256 bits long**.
   169  
   170  To apply JWT protection to your routes, start by adding the following content to your configuration:
   171  
   172  ```json
   173  {
   174    ...
   175    "auth": {
   176      "jwt": {
   177        "expiry": 300,
   178        "secret": "jwt-secret"
   179      }
   180    }
   181  }
   182  ```
   183  
   184  Then, add the following middleware:
   185  
   186  ``` go
   187  authenticator := auth.Middleware(&model.User{}, &auth.JWTAuthenticator{})
   188  router.Middleware(authenticator)
   189  ```
   190  
   191  To request a protected route, you will need to add the following header:
   192  ```
   193  Authorization: Bearer <YOUR_TOKEN>
   194  ```
   195  
   196  ---
   197  
   198  This Authenticator comes with a built-in login controller for password grant, using the field tags explained earlier. You can register the `/auth/login` route using the helper function `auth.JTWRoutes(router)`.
   199  
   200  #### auth.JWTRoutes
   201  
   202  Create a `/auth` route group and registers the `POST /auth/login` validated route. Returns the new route group.
   203  
   204  Validation rules are as follows:
   205  - `username`: required string
   206  - `password`: required string
   207  
   208  The given model is used for username and password retrieval and for instantiating an authenticated request's user.
   209  
   210  Ensure that the given router **is not** protected by JWT authentication, otherwise your users wouldn't be able to log in.
   211  
   212  | Parameters              | Return           |
   213  |-------------------------|------------------|
   214  | `router *goyave.Router` | `*goyave.Router` |
   215  | `model interface{}`     |                  |
   216  
   217  **Example:**
   218  ``` go
   219  func Register(router *goyave.Router) {
   220  	auth.JWTRoutes(router, &model.User{})
   221  }
   222  ```
   223  
   224  #### auth.NewJWTController
   225  
   226  If you want or need ot register the routes yourself, you can instantiate a new JWTController using `auth.NewJWTController()`.
   227  
   228  This function creates a new `JWTController` that will be using the given model for login and token generation.
   229  
   230  A `JWTController` contains one handler called `Login`.
   231  
   232  | Parameters          | Return                |
   233  |---------------------|-----------------------|
   234  | `model interface{}` | `*auth.JWTController` |
   235  
   236  **Example:**
   237  ``` go
   238  jwtRouter := router.Subrouter("/auth")
   239  jwtRouter.Route("POST", "/login", auth.NewJWTController(&model.User{}).Login).Validate(validation.RuleSet{
   240  	"username": {"required", "string"},
   241  	"password": {"required", "string"},
   242  })
   243  ```
   244  
   245  #### auth.GenerateToken
   246  
   247  You may need to generate a token yourself outside of the login route. This function generates a new JWT.
   248  
   249  The token is created using the HMAC SHA256 method and signed using the `auth.jwt.secret` config entry.  
   250  The token is set to expire in the amount of seconds defined by the `auth.jwt.expiry` config entry.
   251  
   252  The generated token will contain the following claims:
   253  - `userid`: has the value of the `id` parameter
   254  - `nbf`: "Not before", the current timestamp is used
   255  - `exp`: "Expriy", the current timestamp plus the `auth.jwt.expiry` config entry.
   256  
   257  | Parameters       | Return   |
   258  |------------------|----------|
   259  | `id interface{}` | `string` |
   260  |                  | `error`  |
   261  
   262  **Example:**
   263  ``` go
   264  token, err := auth.GenerateToken(user.ID)
   265  if err != nil {
   266  	panic(err)
   267  }
   268  fmt.Println(token)
   269  ```
   270  
   271  ### Writing custom Authenticator
   272  
   273  The Goyave authentication system is expandable, meaning that you can implement more authentication methods by creating a new `Authenticator`.
   274  
   275  The typical `Authenticator` is an empty struct implementing the `Authenticator` interface:
   276  ``` go
   277  type MyAuthenticator struct{}
   278  
   279  // Ensure you're correctly implementing Authenticator.
   280  var _ Authenticator = (*MyAuthenticator)(nil) // implements Authenticator
   281  ```
   282  
   283  The next step is to implement the `Authenticate` method. Its purpose is explained at the start of this guide.
   284  
   285  In this example, we are going to authenticate the user using a simple token stored in the database.
   286  ``` go
   287  func (a *MyAuthenticator) Authenticate(request *goyave.Request, user interface{}) error {
   288  	token, ok := request.BearerToken()
   289  
   290  	if !ok {
   291  		return fmt.Errorf(lang.Get(request.Lang, "auth.no-credentials-provided"))
   292  	}
   293  
   294  	// Find the struct field tagged with `auth:"token"`
   295  	columns := auth.FindColumns(user, "token")
   296  
   297  	// Find the user in the database using its token
   298  	result := database.GetConnection().Where(columns[0].Name+" = ?", token).First(user)
   299  
   300  	if errors := result.GetErrors(); len(errors) != 0 && !gorm.IsRecordNotFoundError(result.Error) {
   301  		// Database error
   302  		panic(errors)
   303  	}
   304  
   305  	if result.RecordNotFound() {
   306  		// User not found, return "These credentials don't match our records."
   307  		return fmt.Errorf(lang.Get(request.Lang, "auth.invalid-credentials"))
   308  	}
   309  
   310  	// Authentication successful
   311  	return nil
   312  }
   313  ```
   314  
   315  #### auth.FindColumns
   316  
   317  Find columns in the given struct. A field matches if it has a "auth" tag with the given value.
   318  Returns a slice of found fields, ordered as the input `fields` slice.
   319  
   320  Promoted fields are matched as well.
   321  
   322  If the nth field is not found, the nth value of the returned slice will be `nil`.
   323  
   324  | Parameters          | Return           |
   325  |---------------------|------------------|
   326  | `strct interface{}` | `[]*auth.Column` |
   327  | `fields ...string`  |                  |
   328  
   329  **Example**:
   330  
   331  Given the following struct and `username`, `notatag`, `password`:
   332  
   333  ``` go
   334  type TestUser struct {
   335  	gorm.Model
   336  	Name     string `gorm:"type:varchar(100)"`
   337  	Password string `gorm:"type:varchar(100)" auth:"password"`
   338  	Email    string `gorm:"type:varchar(100);unique_index" auth:"username"`
   339  }
   340  ```
   341  
   342  ``` go
   343  fields := auth.FindColumns(user, "username", "notatag", "password")
   344  ```
   345  
   346  The result will be the `Email` field, `nil` and the `Password` field.
   347  
   348  ::: tip
   349  The `Column` struct is defined as follows:
   350  ``` go
   351  type Column struct {
   352  	Name  string
   353  	Field *reflect.StructField
   354  }
   355  ```
   356  :::
   357  
   358  ## Permissions
   359  
   360  <p style="text-align: center">
   361      <img :src="$withBase('/undraw_in_progress_ql66.svg')" height="150" alt="In progress">
   362  </p>
   363  
   364  ::: warning
   365  This feature is not implemented yet and is coming in a future release.
   366  
   367  [Watch](https://github.com/System-Glitch/goyave) the github repository to stay updated.
   368  :::