Skip to content

Commit 2780468

Browse files
authored
Merge pull request #177 from arangodb-helper/feature/auth-header-token
Added authentication helpers
2 parents 4cd7bd7 + 59ea087 commit 2780468

File tree

4 files changed

+164
-10
lines changed

4 files changed

+164
-10
lines changed

auth.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2018 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
// Author Ewout Prangsma
21+
//
22+
23+
package main
24+
25+
import (
26+
"fmt"
27+
"io/ioutil"
28+
"strings"
29+
30+
"github.com/spf13/cobra"
31+
32+
service "github.com/arangodb-helper/arangodb/service"
33+
)
34+
35+
var (
36+
cmdAuth = &cobra.Command{
37+
Use: "auth",
38+
Short: "ArangoDB authentication helper commands",
39+
Run: cmdShowUsage,
40+
}
41+
cmdAuthHeader = &cobra.Command{
42+
Use: "header",
43+
Short: "Create a full HTTP Authorization header for accessing an ArangoDB server",
44+
Run: cmdAuthHeaderRun,
45+
}
46+
cmdAuthToken = &cobra.Command{
47+
Use: "token",
48+
Short: "Create a JWT authentication token for accessing an ArangoDB server",
49+
Run: cmdAuthTokenRun,
50+
}
51+
authOptions struct {
52+
jwtSecretFile string
53+
user string
54+
}
55+
)
56+
57+
func init() {
58+
cmdMain.AddCommand(cmdAuth)
59+
cmdAuth.AddCommand(cmdAuthHeader)
60+
cmdAuth.AddCommand(cmdAuthToken)
61+
62+
pf := cmdAuth.PersistentFlags()
63+
pf.StringVar(&authOptions.jwtSecretFile, "auth.jwt-secret", "", "name of a plain text file containing a JWT secret used for server authentication")
64+
pf.StringVar(&authOptions.user, "auth.user", "", "name of a user to authenticate as. If empty, 'super-user' authentication is used")
65+
}
66+
67+
// mustAuthCreateJWTToken creates a the JWT token based on authentication options.
68+
// On error the process is exited with a non-zero exit code.
69+
func mustAuthCreateJWTToken() string {
70+
authOptions.jwtSecretFile = mustExpand(authOptions.jwtSecretFile)
71+
72+
if authOptions.jwtSecretFile == "" {
73+
log.Fatal().Msg("A JWT secret file is required. Set --auth.jwt-secret option.")
74+
}
75+
content, err := ioutil.ReadFile(authOptions.jwtSecretFile)
76+
if err != nil {
77+
log.Fatal().Err(err).Msgf("Failed to read JWT secret file '%s'", authOptions.jwtSecretFile)
78+
}
79+
jwtSecret := strings.TrimSpace(string(content))
80+
token, err := service.CreateJwtToken(jwtSecret, authOptions.user)
81+
if err != nil {
82+
log.Fatal().Err(err).Msg("Failed to create JWT token")
83+
}
84+
return token
85+
}
86+
87+
// cmdAuthHeaderRun prints a JWT authorization header on stdout and exits.
88+
func cmdAuthHeaderRun(cmd *cobra.Command, args []string) {
89+
token := mustAuthCreateJWTToken()
90+
fmt.Printf("%s: %s%s\n", service.AuthorizationHeader, service.BearerPrefix, token)
91+
}
92+
93+
// cmdAuthTokenRun prints a JWT authorization token on stdout and exits.
94+
func cmdAuthTokenRun(cmd *cobra.Command, args []string) {
95+
token := mustAuthCreateJWTToken()
96+
fmt.Println(token)
97+
}

docs/Manual/Programs/Starter/Options.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,15 @@ and pass it through the `--auth.jwt-secret-path` option.
9494
For example:
9595

9696
```bash
97-
echo "MakeThisSecretMuchStronger" > jwtSecret
97+
arangodb create jwt-secret --secret=jwtSecret
9898
arangodb --auth.jwt-secret=./jwtSecret
9999
```
100100

101101
All starters used in the cluster must have the same JWT secret.
102102

103+
To use a JWT secret to access the database, use `arangodb auth header`.
104+
See [Using authentication tokens](./Security.md#using-authentication-tokens) for details.
105+
103106
## SSL options
104107

105108
The arango starter by default creates a cluster that uses no unencrypted connections (no SSL).

docs/Manual/Programs/Starter/Security.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,34 @@ arangodb create jwt-secret \
9999
```
100100

101101
Make sure to protect and store the generated file (`my-secret.jwt`) in a safe place.
102+
103+
## Using authentication tokens
104+
105+
ArangoDB deployments that require authentication can be accessed through standard user+password
106+
pairs or using a JWT to get "super-user" access.
107+
108+
This super-user access is needed to communicate directly with the agency or with any server
109+
in the deployment.
110+
Note that uses super-user access for normal database access is NOT advised.
111+
112+
To create a JWT from the JWT secret file specified using the `--auth.jwt-secret` option,
113+
use the following command:
114+
115+
```bash
116+
arangodb auth token --auth.jwt-secret=<secret-file>
117+
```
118+
119+
To create a complete HTTP Authorization header that can be passed directly to tools like `curl`,
120+
use the following command:
121+
122+
```bash
123+
arangodb auth header --auth.jwt-secret=<secret-file>
124+
```
125+
126+
Using `curl` with this command looks like this:
127+
128+
```bash
129+
curl -v -H "$(arangodb auth header --auth.jwt-secret=<secret-file>)" http://<database-ip>:8529/_api/version
130+
```
131+
132+
Note the double quotes around `$(...)`.

service/authentication.go

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,50 @@ import (
2828
jwt "github.com/dgrijalva/jwt-go"
2929
)
3030

31-
// addJwtHeader calculates a JWT authorization header based on the given secret
32-
// and adds it to the given request.
33-
// If the secret is empty, nothing is done.
34-
func addJwtHeader(req *http.Request, jwtSecret string) error {
31+
const (
32+
AuthorizationHeader = "Authorization"
33+
BearerPrefix = "bearer "
34+
)
35+
36+
// CreateJwtToken calculates a JWT authorization token based on the given secret.
37+
// If the secret is empty, an empty token is returned.
38+
func CreateJwtToken(jwtSecret, user string) (string, error) {
3539
if jwtSecret == "" {
36-
return nil
40+
return "", nil
3741
}
3842
// Create a new token object, specifying signing method and the claims
3943
// you would like it to contain.
40-
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
44+
claims := jwt.MapClaims{
4145
"iss": "arangodb",
4246
"server_id": "foo",
43-
})
47+
}
48+
if user != "" {
49+
claims["preferred_username"] = user
50+
}
51+
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
4452

4553
// Sign and get the complete encoded token as a string using the secret
4654
signedToken, err := token.SignedString([]byte(jwtSecret))
55+
if err != nil {
56+
return "", maskAny(err)
57+
}
58+
59+
return signedToken, nil
60+
}
61+
62+
// addJwtHeader calculates a JWT authorization header based on the given secret
63+
// and adds it to the given request.
64+
// If the secret is empty, nothing is done.
65+
func addJwtHeader(req *http.Request, jwtSecret string) error {
66+
if jwtSecret == "" {
67+
return nil
68+
}
69+
signedToken, err := CreateJwtToken(jwtSecret, "")
4770
if err != nil {
4871
return maskAny(err)
4972
}
5073

51-
req.Header.Set("Authorization", "bearer "+signedToken)
74+
req.Header.Set(AuthorizationHeader, BearerPrefix+signedToken)
5275
return nil
5376
}
5477

@@ -60,6 +83,6 @@ func addBearerTokenHeader(req *http.Request, bearerToken string) error {
6083
return nil
6184
}
6285

63-
req.Header.Set("Authorization", "bearer "+bearerToken)
86+
req.Header.Set(AuthorizationHeader, BearerPrefix+bearerToken)
6487
return nil
6588
}

0 commit comments

Comments
 (0)