feat: Add auth.tokenFile support for file-based token authentication

This commit is contained in:
xufeiran 2024-08-17 08:58:46 +08:00
parent 03c8d7bf96
commit b2a468aa55
7 changed files with 124 additions and 2 deletions

View File

@ -603,6 +603,33 @@ When specifying `auth.method = "token"` in `frpc.toml` and `frps.toml` - token b
Make sure to specify the same `auth.token` in `frps.toml` and `frpc.toml` for frpc to pass frps validation
#### File-based Token Authentication
If you prefer not to include the token directly in the configuration file, you can specify the path to a file containing the token using the `auth.tokenFile` option.
Instead of setting `auth.token`, specify the path to the file containing your token with `auth.tokenFile`.
Ensure that the file contains only the token and no additional content. Example configuration:
```toml
# frpc.toml
[common]
auth.tokenFile = "/path/to/your/token_file"
```
```toml
# frps.toml
[common]
auth.tokenFile = "/path/to/your/token_file"
```
Make sure the file at the specified path is readable by the application and contains the following:
``` txt
your_secret_token
```
**Notes:** If both `auth.token` and `auth.tokenFile` are specified, the token from `auth.token` will be used.
#### OIDC Authentication
When specifying `auth.method = "oidc"` in `frpc.toml` and `frps.toml` - OIDC based authentication will be used.

View File

@ -165,6 +165,7 @@ func RegisterClientCommonConfigFlags(cmd *cobra.Command, c *v1.ClientCommonConfi
}
cmd.PersistentFlags().StringVarP(&c.User, "user", "u", "", "user")
cmd.PersistentFlags().StringVarP(&c.Auth.Token, "token", "t", "", "auth token")
cmd.PersistentFlags().StringVarP(&c.Auth.TokenFile, "token_file", "", "", "auth token file")
}
type PortsRangeSliceFlag struct {
@ -240,6 +241,7 @@ func RegisterServerConfigFlags(cmd *cobra.Command, c *v1.ServerConfig, opts ...R
cmd.PersistentFlags().Int64VarP(&c.Log.MaxDays, "log_max_days", "", 3, "log max days")
cmd.PersistentFlags().BoolVarP(&c.Log.DisablePrintColor, "disable_log_color", "", false, "disable log color in console")
cmd.PersistentFlags().StringVarP(&c.Auth.Token, "token", "t", "", "auth token")
cmd.PersistentFlags().StringVarP(&c.Auth.TokenFile, "token_file", "", "", "auth token file")
cmd.PersistentFlags().StringVarP(&c.SubDomainHost, "subdomain_host", "", "", "subdomain host")
cmd.PersistentFlags().VarP(&PortsRangeSliceFlag{V: &c.AllowPorts}, "allow_ports", "", "allow ports")
cmd.PersistentFlags().Int64VarP(&c.MaxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")

View File

@ -103,6 +103,15 @@ func LoadFileContentWithTemplate(path string, values *Values) ([]byte, error) {
return RenderWithTemplate(b, values)
}
func LoadFileToken(path string) (string, error) {
data, err := os.ReadFile(path)
if err != nil {
return "", err
}
token := strings.TrimSpace(string(data))
return token, nil
}
func LoadConfigureFromFile(path string, c any, strict bool) error {
content, err := LoadFileContentWithTemplate(path, GetValues())
if err != nil {

View File

@ -179,12 +179,20 @@ type AuthClientConfig struct {
// Token specifies the authorization token used to create keys to be sent
// to the server. The server must have a matching token for authorization
// to succeed. By default, this value is "".
Token string `json:"token,omitempty"`
OIDC AuthOIDCClientConfig `json:"oidc,omitempty"`
Token string `json:"token,omitempty"`
TokenFile string `json:"tokenFile,omitempty"`
OIDC AuthOIDCClientConfig `json:"oidc,omitempty"`
}
func (c *AuthClientConfig) Complete() {
c.Method = util.EmptyOr(c.Method, "token")
if c.Token == "" && c.TokenFile != "" {
tokenFromFile, err := util.LoadFileToken(c.TokenFile)
if err == nil {
c.Token = tokenFromFile
}
}
}
type AuthOIDCClientConfig struct {

View File

@ -126,11 +126,18 @@ type AuthServerConfig struct {
Method AuthMethod `json:"method,omitempty"`
AdditionalScopes []AuthScope `json:"additionalScopes,omitempty"`
Token string `json:"token,omitempty"`
TokenFile string `json:"tokenFile,omitempty"`
OIDC AuthOIDCServerConfig `json:"oidc,omitempty"`
}
func (c *AuthServerConfig) Complete() {
c.Method = util.EmptyOr(c.Method, "token")
if c.Token == "" && c.TokenFile != "" {
tokenFromFile, err := util.LoadFileToken(c.TokenFile)
if err == nil {
c.Token = tokenFromFile
}
}
}
type AuthOIDCServerConfig struct {

View File

@ -22,6 +22,7 @@ import (
"fmt"
mathrand "math/rand/v2"
"net"
"os"
"strconv"
"strings"
"time"
@ -134,3 +135,12 @@ func RandomSleep(duration time.Duration, minRatio, maxRatio float64) time.Durati
func ConstantTimeEqString(a, b string) bool {
return subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
}
func LoadFileToken(path string) (string, error) {
data, err := os.ReadFile(path)
if err != nil {
return "", err
}
token := strings.TrimSpace(string(data))
return token, nil
}

View File

@ -2,6 +2,7 @@ package basic
import (
"fmt"
"os"
"strings"
"time"
@ -149,6 +150,64 @@ var _ = ginkgo.Describe("[Feature: Client-Server]", func() {
client: `auth.token = "invalid"`,
expectError: true,
})
defineClientServerTest("Client Token File Correct", f, &generalTestConfigures{
server: `auth.token = "123456"`,
client: func() string {
tokenFile := f.WriteTempFile("correct_token.txt", "123456")
framework.AddCleanupAction(func() {
os.Remove(tokenFile)
})
return fmt.Sprintf(`auth.tokenFile = "%s"`, tokenFile)
}(),
})
defineClientServerTest("Server Client Token File Correct", f, &generalTestConfigures{
server: func() string {
tokenFile := f.WriteTempFile("correct_token.txt", "123456")
framework.AddCleanupAction(func() {
os.Remove(tokenFile)
})
return fmt.Sprintf(`auth.tokenFile = "%s"`, tokenFile)
}(),
client: `auth.token = "123456"`,
})
defineClientServerTest("Client Token File Incorrect", f, &generalTestConfigures{
server: `auth.token = "123456"`,
client: func() string {
tokenFile := f.WriteTempFile("incorrect_token.txt", "invalid")
framework.AddCleanupAction(func() {
os.Remove(tokenFile)
})
return fmt.Sprintf(`auth.tokenFile = "%s"`, tokenFile)
}(),
expectError: true,
})
defineClientServerTest("Server Token File Incorrect", f, &generalTestConfigures{
server: func() string {
tokenFile := f.WriteTempFile("incorrect_token.txt", "invalid")
framework.AddCleanupAction(func() {
os.Remove(tokenFile)
})
return fmt.Sprintf(`auth.tokenFile = "%s"`, tokenFile)
}(),
client: `auth.token = "123456"`,
expectError: true,
})
defineClientServerTest("Client Token File Not Exist", f, &generalTestConfigures{
server: `auth.token = "123456"`,
client: `auth.tokenFile = "/path/to/non/existent/file"`,
expectError: true,
})
defineClientServerTest("Server Token File Not Exist", f, &generalTestConfigures{
server: `auth.tokenFile = "/path/to/non/existent/file"`,
client: `auth.token = "123456"`,
expectError: true,
})
})
ginkgo.Describe("TLS", func() {