loginsrv

Unnamed repository; edit this file 'description' to name the repository.
git clone git@jamesshield.xyz:repos/loginsrv.git
Log | Files | Refs | README | LICENSE

README.md (24341B)


      1 # loginsrv
      2 
      3 loginsrv is a standalone minimalistic login server providing a [JWT](https://jwt.io/) login for multiple login backends.
      4 
      5 [![Docker](https://img.shields.io/docker/pulls/tarent/loginsrv.svg)](https://hub.docker.com/r/tarent/loginsrv/)
      6 [![Build Status](https://api.travis-ci.org/tarent/loginsrv.svg?branch=master)](https://travis-ci.org/tarent/loginsrv)
      7 [![Go Report Card](https://goreportcard.com/badge/github.com/tarent/loginsrv)](https://goreportcard.com/report/github.com/tarent/loginsrv)
      8 [![Coverage Status](https://coveralls.io/repos/github/tarent/loginsrv/badge.svg?branch=master)](https://coveralls.io/github/tarent/loginsrv?branch=master)
      9 [![Join the chat at https://gitter.im/tarent/loginsrv](https://badges.gitter.im/tarent/loginsrv.svg)](https://gitter.im/tarent/loginsrv?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
     10 
     11 
     12 __** Attention: Update to v1.3.0 for Google Login Update !!!! **__
     13 
     14 Google will stop support for the Google+ APIs. So we changed loginsrv to use the standard oauth endpoints for Google login.
     15 Please update loginsrv to v1.3.0 if you are using google login.
     16 
     17 __** Attention: Since v1.3.0, pure HTTP is not supported by default **__
     18 
     19 Since v1.3.0, loginsrv sets the secure flag for the login cookie. So, if you use HTTP fo connect with the browser, e.g. for testing, you browser will ignore the cookie.
     20 Use the flag `-cookie-secure=false` when testing without HTTPS.
     21 
     22 ## Abstract
     23 
     24 Loginsrv provides a minimal endpoint for authentication. The login is performed against the providers and returned as a JSON Web Token (JWT).
     25 It can be used as:
     26 
     27 * Standalone microservice
     28 * Docker container
     29 * Golang library
     30 * [Caddy](http://caddyserver.com/) plugin.  (See [caddy/README.md](./caddy/README.md) for details)
     31 
     32 ![](.screenshot.png)
     33 
     34 ## Supported Provider Backends
     35 The following providers (login backends) are supported.
     36 
     37 * [Htpasswd](#htpasswd)
     38 * [OSIAM](#osiam)
     39 * [Simple](#simple) (user/password pairs by configuration)
     40 * [Httpupstream](#httpupstream)
     41 * [OAuth2](#oauth2)
     42   * GitHub login
     43   * Google login
     44   * Bitbucket login
     45   * Facebook login
     46   * Gitlab login
     47 
     48 ## Questions
     49 
     50 For questions and support please use the [Gitter chat room](https://gitter.im/tarent/loginsrv).
     51 
     52 [![Join the chat at https://gitter.im/tarent/loginsrv](https://badges.gitter.im/tarent/loginsrv.svg)](https://gitter.im/tarent/loginsrv?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
     53 
     54 ## Configuration and Startup
     55 ### Config Options
     56 
     57 _Note for Caddy users_: Not all parameters are available in Caddy. See the table for details. With Caddy, the parameter names can also be used with `_` in the names, e.g. `cookie_http_only`.
     58 
     59 | Parameter                   | Type        | Default      | Caddy | Description                                                                                           |
     60 |-----------------------------|-------------|--------------|-------|-------------------------------------------------------------------------------------------------------|
     61 | -cookie-domain              | string      |              | X     | Optional domain parameter for the cookie                                                              |
     62 | -cookie-expiry              | string      | session      | X     | Expiry duration for the cookie, e.g. 2h or 3h30m                                                      |
     63 | -cookie-http-only           | boolean     | true         | X     | Set the cookie with the HTTP only flag                                                                |
     64 | -cookie-name                | string      | "jwt_token"  | X     | Name of the JWT cookie                                                                                |
     65 | -cookie-secure              | boolean     | true         | X     | Set the secure flag on the JWT cookie. (Set this to false for plain HTTP support)                     |
     66 | -github                     | value       |              | X     | OAuth config in the form: client_id=..,client_secret=..[,scope=..][,redirect_uri=..]                  |
     67 | -google                     | value       |              | X     | OAuth config in the form: client_id=..,client_secret=..[,scope=..][,redirect_uri=..]                  |
     68 | -bitbucket                  | value       |              | X     | OAuth config in the form: client_id=..,client_secret=..[,scope=..][,redirect_uri=..]                  |
     69 | -facebook                   | value       |              | X     | OAuth config in the form: client_id=..,client_secret=..[,scope=..][,redirect_uri=..]                  |
     70 | -gitlab                     | value       |              | X     | OAuth config in the form: client_id=..,client_secret=..[,scope=..,][redirect_uri=..]                  |
     71 | -host                       | string      | "localhost"  | -     | Host to listen on                                                                                     |
     72 | -htpasswd                   | value       |              | X     | Htpasswd login backend opts: file=/path/to/pwdfile                                                    |
     73 | -jwt-expiry                 | go duration | 24h          | X     | Expiry duration for the JWT token, e.g. 2h or 3h30m                                                   |
     74 | -jwt-secret                 | string      | "random key" | X     | Secret used to sign the JWT token. (See [caddy/README.md](./caddy/README.md) for details.)            |
     75 | -jwt-secret-file            | string      |              | X     | File to load the jwt-secret from, e.g. `/run/secrets/some.key`. **Takes precedence over jwt-secret!** |
     76 | -jwt-algo                   | string      | "HS512"      | X     | Signing algorithm to use (ES256, ES384, ES512, RS256, RS384, RS512, HS256, HS384, HS512)              |
     77 | -log-level                  | string      | "info"       | -     | Log level                                                                                             |
     78 | -login-path                 | string      | "/login"     | X     | Path of the login resource                                                                            |
     79 | -logout-url                 | string      |              | X     | URL or path to redirect to after logout                                                               |
     80 | -osiam                      | value       |              | X     | OSIAM login backend opts: endpoint=..,client_id=..,client_secret=..                                   |
     81 | -port                       | string      | "6789"       | -     | Port to listen on                                                                                     |
     82 | -redirect                   | boolean     | true         | X     | Allow dynamic overwriting of the the success by query parameter                                       |
     83 | -redirect-query-parameter   | string      | "backTo"     | X     | URL parameter for the redirect target                                                                 |
     84 | -redirect-check-referer     | boolean     | true         | X     | Check the referer header to ensure it matches the host header on dynamic redirects                    |
     85 | -redirect-host-file         | string      | ""           | X     | A file containing a list of domains that redirects are allowed to, one domain per line                |
     86 | -simple                     | value       |              | X     | Simple login backend opts: user1=password,user2=password,..                                           |
     87 | -success-url                | string      | "/"          | X     | URL to redirect to after login                                                                        |
     88 | -template                   | string      |              | X     | An alternative template for the login form                                                            |
     89 | -text-logging               | boolean     | true         | -     | Log in text format instead of JSON                                                                    |
     90 | -jwt-refreshes              | int         | 0            | X     | The maximum number of JWT refreshes                                                                   |
     91 | -grace-period               | go duration | 5s           | -     | Duration to wait after SIGINT/SIGTERM for existing requests. No new requests are accepted.            |
     92 | -user-file                  | string      |              | X     | A YAML file with user specific data for the tokens. (see below for an example)                        |
     93 | -user-endpoint              | string      |              | X     | URL of an endpoint providing user specific data for the tokens. (see below for an example)            |
     94 | -user-endpoint-token        | string      |              | X     | Authentication token used when communicating with the user endpoint                                   |
     95 | -user-endpoint-timeout      | go duration | 5s           | X     | Timeout used when communicating with the user endpoint                                                |
     96 
     97 ### Environment Variables
     98 All of the above Config Options can also be applied as environment variables by using variables named this way: `LOGINSRV_OPTION_NAME`.
     99 So e.g. `jwt-secret` can be set by environment variable `LOGINSRV_JWT_SECRET`.
    100 
    101 ### Startup Examples
    102 The simplest way to use loginsrv is by the provided docker container.
    103 E.g. configured with the simple provider:
    104 ```sh
    105 $ docker run -d -p 8080:8080 tarent/loginsrv -cookie-secure=false -jwt-secret my_secret -simple bob=secret
    106 
    107 $ curl --data "username=bob&password=secret" 127.0.0.1:8080/login
    108 eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJib2IifQ.uWoJkSXTLA_RvfLKe12pb4CyxQNxe5_Ovw-N5wfQwkzXz2enbhA9JZf8MmTp9n-TTDcWdY3Fd1SA72_M20G9lQ
    109 ```
    110 
    111 The same configuration could be written with environment variables this way:
    112 ```sh
    113 $ docker run -d -p 8080:8080 -E COOKIE_SECURE=false -e LOGINSRV_JWT_SECRET=my_secret -e LOGINSRV_BACKEND=provider=simple,bob=secret tarent/loginsrv
    114 ```
    115 
    116 ## API
    117 
    118 ### GET /login
    119 
    120 Per default, it returns a simple bootstrap styled login form for unauthenticated requests and a page with user info for authenticated requests.
    121 When the call accepts a JSON output, the json content of the token is returned to authenticated requests.
    122 
    123 The returned HTML follows the UI composition conventions from (lib-compose)[https://github.com/tarent/lib-compose],
    124 so it can be embedded into an existing layout.
    125 
    126 | Parameter-Type    | Parameter                                        | Description                                                       |              | 
    127 | ------------------|--------------------------------------------------|-------------------------------------------------------------------|--------------|
    128 | Http-Header       | Accept: text/html                                | Return the login form or user html.                                | default      |
    129 | Http-Header       | Accept: application/json                         | Return the user Object as json, or 403 if not authenticated.      |              |
    130 
    131 ### GET /login/<provider>
    132 
    133 Starts the OAuth Web Flow with the configured provider. E.g. `GET /login/github` redirects to the GitHub login form.
    134 
    135 ### POST /login
    136 
    137 Performs the login and returns the JWT. Depending on the content-type and parameters, a classical JSON-Rest or a redirect can be performed.
    138 
    139 #### Runtime Parameters
    140 
    141 | Parameter-Type    | Parameter                                        | Description                                                       |              | 
    142 | ------------------|--------------------------------------------------|-------------------------------------------------------------------|--------------|
    143 | Http-Header       | Accept: text/html                                | Set the JWT as a cookie named 'jwt_token'                         | default      |
    144 | Http-Header       | Accept: application/jwt                          | Returns the JWT within the body. No cookie is set                 |              |
    145 | Http-Header       | Content-Type: application/x-www-form-urlencoded  | Expect the credentials as form encoded parameters                 | default      |
    146 | Http-Header       | Content-Type: application/json                   | Take the credentials from the provided JSON object                |              |
    147 | Post-Parameter    | username                                         | The username                                                      |              |
    148 | Post-Parameter    | password                                         | The password                                                      |              |
    149 | Get or Post       | backTo                                           | Dynamic redirect target after login (see (Redirects)[#redirects]) | -success-url |
    150 
    151 #### Possible Return Codes
    152 
    153 | Code | Meaning               | Description                                                                                                               |
    154 |------| ----------------------|---------------------------------------------------------------------------------------------------------------------------|
    155 | 200  | OK                    | Successfully authenticated                                                                                                |
    156 | 403  | Forbidden             | The credentials are wrong                                                                                                 |
    157 | 400  | Bad Request           | Missing parameters                                                                                                        |
    158 | 500  | Internal Server Error | Internal error, e.g. the login provider is not available or failed                                                        |
    159 | 303  | See Other             | Sets the JWT as a cookie, if the login succeeds and redirect to the URLs provided in `redirectSuccess` or `redirectError` |
    160 
    161 Hint: The status `401 Unauthorized` is not used as a return code to not conflict with an HTTP Basic authentication.
    162 
    163 #### JWT-Refresh
    164 
    165 If the POST-Parameters for username and password are missing and a valid JWT-Cookie is part of the request, then the JWT-Cookie is refreshed.
    166 This only happens if the jwt-refreshes config option is set to a value greater than 0. 
    167 
    168 ### DELETE /login
    169 
    170 Deletes the JWT cookie.
    171 
    172 For simple usage in web applications, this can also be called by `GET|POST /login?logout=true`
    173 
    174 ### API Examples
    175 
    176 #### Example:
    177 Default is to return the token as Content-Type application/jwt within the body.
    178 ```sh
    179 curl -i --data "username=bob&password=secret" http://127.0.0.1:6789/login
    180 HTTP/1.1 200 OK
    181 Content-Type: application/jwt
    182 Date: Mon, 14 Nov 2016 21:35:42 GMT
    183 Content-Length: 100
    184 
    185 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJib2IifQ.-51G5JQmpJleARHp8rIljBczPFanWT93d_N_7LQGUXU
    186 ```
    187 
    188 #### Example: Credentials as JSON
    189 The credentials can also be sent JSON encoded.
    190 ```sh
    191 curl -i -H 'Content-Type: application/json'  --data '{"username": "bob", "password": "secret"}' http://127.0.0.1:6789/login
    192 HTTP/1.1 200 OK
    193 Content-Type: application/jwt
    194 Date: Mon, 14 Nov 2016 21:35:42 GMT
    195 Content-Length: 100
    196 
    197 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJib2IifQ.-51G5JQmpJleARHp8rIljBczPFanWT93d_N_7LQGUXU
    198 ```
    199 
    200 #### Example: web based flow with 'Accept: text/html'
    201 Sets the JWT as a cookie and redirects to a web page.
    202 ```sh
    203 curl -i -H 'Accept: text/html' --data "username=bob&password=secret" http://127.0.0.1:6789/login
    204 HTTP/1.1 303 See Other
    205 Location: /
    206 Set-Cookie: jwt_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJib2IifQ.-51G5JQmpJleARHp8rIljBczPFanWT93d_N_7LQGUXU; HttpOnly
    207 ```
    208 
    209 #### Example: AJAX call with JQuery to fetch a JWT token and create a cookie from it
    210 Creates a cookie from a successful API call to login.
    211 ```js
    212 $.ajax({
    213 	url: "http://localhost:8080/login",
    214 	type: 'POST',
    215 	dataType: 'text',
    216 	contentType: 'application/json',
    217 	data: JSON.stringify( { 
    218 		'username': 'demo', 
    219 		'password': 'demo'
    220 	}),
    221 	success: function(data) {
    222 		document.cookie = "jwt_token=" + data + ";path=/";
    223 	},
    224 	error: function (xhr, ajaxOptions, thrownError) {
    225 	}
    226 });
    227 ```
    228 Make sure your main page has JQuery:
    229 ```html
    230 <script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
    231 ```
    232 
    233 ### Redirects
    234 
    235 The API has support for a redirect query parameter, e.g. `?backTo=/dynamic/return/path`. For security reasons, the default behaviour is very restrictive:
    236 
    237 * Only local redirects (same host) are allowed.
    238 * The `Referer` header is checked to ensure that the call to the login page came from the same page.
    239 
    240 These restrictions are there, to prevent you from unchecked redirect attacks, e.g. phishing or login attacks.
    241 If you know, what you are doing, you can disable the `Referer` check with `--redirect-check-referer=false` and provide a whitelist file
    242 for allowed external domains with `--redirect-host-file=/some/domains.txt`.
    243 
    244 ## The JWT Token
    245 Depending on the provider, the token may look as follows:
    246 ```json
    247 {
    248   "sub": "smancke",
    249   "picture": "https://avatars2.githubusercontent.com/u/4291379?v=3",
    250   "name": "Sebastian Mancke",
    251   "email": "s.mancke@tarent.de",
    252   "origin": "github"
    253 }
    254 ```
    255 
    256 ## Provider Backends
    257 
    258 ### Htpasswd
    259 Authentication against htpasswd file. MD5, SHA1 and Bcrypt are supported. But we recommend to only use Bcrypt for security reasons (e.g. `htpasswd -B -C 15`).
    260 
    261 Parameters for the provider:
    262 
    263 | Parameter-Name    | Description                |
    264 | ------------------|----------------------------|
    265 | file              | Path to the password file (multiple files can be used by separating them with ';')  |
    266 
    267 Example:
    268 ```sh
    269 loginsrv -htpasswd file=users
    270 ```
    271 
    272 ### Httpupstream
    273 Authentication against an upstream HTTP server by performing a HTTP Basic authentication request and checking the response for a HTTP 200 OK status code. Anything other than a 200 OK status code will result in a failure to authenticate.
    274 
    275 Parameters for the provider:
    276 
    277 | Parameter-Name    | Description                                                               |
    278 | ------------------|---------------------------------------------------------------------------|
    279 | upstream          | HTTP/HTTPS URL to call                                                    |
    280 | skipverify        | True to ignore TLS errors (optional, false by default)                    |
    281 | timeout           | Request timeout (optional 1m by default, go duration syntax is supported) |
    282 
    283 Example:
    284 ```sh
    285 loginsrv -httpupstream upstream=https://google.com,timeout=1s
    286 ```
    287 
    288 ### OSIAM
    289 [OSIAM](https://github.com/osiam/osiam) is a secure identity management solution providing REST based services for authentication and authorization.
    290 It implements the multiple OAuth2 flows, as well as SCIM for managing the user data.
    291 
    292 To start loginsrv against the default OSIAM configuration on the same machine, use the following example.
    293 ```sh
    294 loginsrv --jwt-secret=jwtsecret --text-logging -osiam endpoint=http://localhost:8080,client_id=example-client,client_secret=secret'
    295 ```
    296 
    297 Then go to http://127.0.0.1:6789/login and login with `admin/koala`.
    298 
    299 ### Simple
    300 Simple is a demo provider for testing only. It holds a user/password table in memory.
    301 
    302 Example
    303 ```sh
    304 loginsrv -simple bob=secret
    305 ```
    306 
    307 ## OAuth2
    308 
    309 The OAuth Web Flow (aka 3-legged-OAuth flow) is also supported.
    310 Currently the following OAuth provider is supported:
    311 
    312 * GitHub
    313 * Google
    314 * Bitbucket
    315 * Facebook
    316 * Gitlab
    317 
    318 An OAuth provider supports the following parameters:
    319 
    320 | Parameter-Name    | Description                            |
    321 | ------------------|----------------------------------------|
    322 | client_id         | OAuth Client ID                        |
    323 | client_secret     | OAuth Client Secret                    |
    324 | scope             | Space separated scope List (optional)  |
    325 | redirect_uri      | Alternative Redirect URI (optional)    |
    326 
    327 When configuring the OAuth parameters at your external OAuth provider, a redirect URI has to be supplied. This redirect URI has to point to the path `/login/<provider>`.
    328 If not supplied, the OAuth redirect URI is calculated out of the current URL. This should work in most cases and should even work
    329 if loginsrv is routed through a reverse proxy, if the headers `X-Forwarded-Host` and `X-Forwarded-Proto` are set correctly.
    330 
    331 ### GitHub Startup Example
    332 ```sh
    333 $ docker run -p 80:80 tarent/loginsrv -github client_id=xxx,client_secret=yyy
    334 ```
    335 
    336 ## Templating
    337 
    338 A custom template can be supplied by the parameter `template`. 
    339 You can find the original template in [login/login_form.go](https://github.com/tarent/loginsrv/blob/master/login/login_form.go).
    340 
    341 The templating uses the Golang template package. A short intro can be found [here](https://astaxie.gitbooks.io/build-web-application-with-golang/en/07.4.html).
    342 
    343 When you specify a custom template, only the layout of the original template is replaced. The partials of the original are still loaded into the template context and can be used by your template. So a minimal unstyled login template could look like this:
    344 
    345 ```html
    346 <!DOCTYPE html>
    347 <html>
    348   <head>
    349       <!-- your styles -->
    350   <head>
    351   <body>
    352       <!-- your header -->
    353 
    354       {{ if .Error}}
    355         <div class="alert alert-danger" role="alert">
    356           <strong>Internal Error. </strong> Please try again later.
    357         </div>
    358       {{end}}
    359 
    360       {{if .Authenticated}}
    361 
    362          {{template "userInfo" . }}
    363 
    364       {{else}}
    365 
    366         {{template "login" . }}
    367 
    368       {{end}}
    369 
    370       <!-- your footer -->
    371   </body>
    372 </html>
    373 ```
    374 
    375 ## Custom claims
    376 
    377 To customize the content of the JWT token either a file wich contains
    378 user data or an endpoint providing claims can be provided.
    379 
    380 ### User file
    381 
    382 A user file is a YAML file which contains additional information which
    383 is encoded in the token. After successful authentication against a
    384 backend system, the user is searched within the file and the content
    385 of the claims parameter is used to enhance the user JWT claim
    386 parameters.
    387 
    388 To match an entry, the user file is searched in linear order and all attributes has to match
    389 the data of the authentication backend. The first matching entry will be used and all parameters
    390 below the claim attribute are written into the token. The following attributes can be used for matching:
    391 * `sub` - the username (all backends)
    392 * `origin` - the provider or backend name (all backends)
    393 * `email` - the mail address (the OAuth provider)
    394 * `domain` - the domain (Google only)
    395 * `groups` - the full path string of user groups enclosed in an array (Gitlab only)
    396 
    397 Example:
    398 * The user bob will become the `"role": "superAdmin"`, when authenticating with htpasswd file
    399 * The user admin@example.org will become `"role": "admin"` and `"projects": ["example"]`, when authenticating with Google OAuth
    400 * All other Google users with the domain example will become `"role": "user"` and `"projects": ["example"]`
    401 * All other Gitlab users with group `example/subgroup` and `othergroup` will become `"role": "admin"`.
    402 * All others will become `"role": "unknown"`, independent of the authentication provider
    403 
    404 ```yaml
    405 - sub: bob
    406   origin: htpasswd
    407   claims:
    408     role: superAdmin
    409 
    410 - email: admin@example.org
    411   origin: Google
    412   claims:
    413     role: admin
    414     projects:
    415       - example
    416 
    417 - domain: example.org
    418   origin: Google
    419   claims:
    420     role: user
    421     projects:
    422       - example
    423 
    424 - groups:
    425     - example/subgroup
    426     - othergroup
    427   origin: gitlab
    428   claims:
    429     role: admin
    430 
    431 - claims:
    432     role: unknown
    433 ```
    434 
    435 ### User endpoint
    436 
    437 A user endpoint is a http endpoint which provides additional
    438 information on an authenticated user. After successful authentication
    439 against a backend system, the endpoint gets called and the provided
    440 information is used to enhance the user JWT claim parameters.
    441 
    442 loginsrv passes these parameters to the endpoint:
    443 * `sub` - the username (all backends)
    444 * `origin` - the provider or backend name (all backends)
    445 * `email` - the mail address (the OAuth provider)
    446 * `domain` - the domain (Google only)
    447 * `groups` - the full path string of user groups enclosed in an array (Gitlab only)
    448 
    449 An interaction looks like this
    450 
    451 ```http
    452 GET /claims?origin=google&sub=test@example.com&email=test@example.com HTTP/1.1
    453 Host: localhost:8080
    454 Accept: */*
    455 Authorization: Bearer token
    456 
    457 HTTP/1.1 200 OK
    458 Content-Type: application/json
    459 
    460 {
    461   "sub":"test@example.com",
    462   "uid":"113",
    463   "origin":"google",
    464   "permissions": ["read", "write"]
    465 }
    466 ```