2019-11-18 16:28:18 +00:00
|
|
|
package pogs
|
|
|
|
|
|
|
|
import (
|
2019-12-04 17:22:08 +00:00
|
|
|
"errors"
|
2019-11-18 16:28:18 +00:00
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
// AuthenticateResponse is the serialized response from the Authenticate RPC.
|
|
|
|
// It's a 1:1 representation of the capnp message, so it's not very useful for programmers.
|
|
|
|
// Instead, you should call the `Outcome()` method to get a programmer-friendly sum type, with one
|
|
|
|
// case for each possible outcome.
|
|
|
|
type AuthenticateResponse struct {
|
|
|
|
PermanentErr string
|
|
|
|
RetryableErr string
|
|
|
|
Jwt []byte
|
|
|
|
HoursUntilRefresh uint8
|
|
|
|
}
|
|
|
|
|
|
|
|
// Outcome turns the deserialized response of Authenticate into a programmer-friendly sum type.
|
|
|
|
func (ar AuthenticateResponse) Outcome() AuthOutcome {
|
|
|
|
// If the user's authentication was unsuccessful, the server will return an error explaining why.
|
|
|
|
// cloudflared should fatal with this error.
|
|
|
|
if ar.PermanentErr != "" {
|
2019-12-04 17:22:08 +00:00
|
|
|
return NewAuthFail(errors.New(ar.PermanentErr))
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there was a network error, then cloudflared should retry later,
|
|
|
|
// because origintunneld couldn't prove whether auth was correct or not.
|
|
|
|
if ar.RetryableErr != "" {
|
|
|
|
return NewAuthUnknown(errors.New(ar.RetryableErr), ar.HoursUntilRefresh)
|
2019-11-18 16:28:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If auth succeeded, return the token and refresh it when instructed.
|
2019-12-04 17:22:08 +00:00
|
|
|
if len(ar.Jwt) > 0 {
|
2019-11-20 18:12:08 +00:00
|
|
|
return NewAuthSuccess(ar.Jwt, ar.HoursUntilRefresh)
|
2019-11-18 16:28:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise the state got messed up.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// AuthOutcome is a programmer-friendly sum type denoting the possible outcomes of Authenticate.
|
|
|
|
//go-sumtype:decl AuthOutcome
|
|
|
|
type AuthOutcome interface {
|
|
|
|
isAuthOutcome()
|
2019-11-18 23:01:20 +00:00
|
|
|
// Serialize into an AuthenticateResponse which can be sent via Capnp
|
|
|
|
Serialize() AuthenticateResponse
|
2019-11-18 16:28:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AuthSuccess means the backend successfully authenticated this cloudflared.
|
|
|
|
type AuthSuccess struct {
|
2019-11-20 18:12:08 +00:00
|
|
|
jwt []byte
|
|
|
|
hoursUntilRefresh uint8
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewAuthSuccess(jwt []byte, hoursUntilRefresh uint8) AuthSuccess {
|
|
|
|
return AuthSuccess{jwt: jwt, hoursUntilRefresh: hoursUntilRefresh}
|
2019-11-18 16:28:18 +00:00
|
|
|
}
|
|
|
|
|
2019-12-04 17:22:08 +00:00
|
|
|
func (ao AuthSuccess) JWT() []byte {
|
|
|
|
return ao.jwt
|
|
|
|
}
|
|
|
|
|
2019-11-18 16:28:18 +00:00
|
|
|
// RefreshAfter is how long cloudflared should wait before rerunning Authenticate.
|
2019-11-20 18:12:08 +00:00
|
|
|
func (ao AuthSuccess) RefreshAfter() time.Duration {
|
|
|
|
return hoursToTime(ao.hoursUntilRefresh)
|
2019-11-18 16:28:18 +00:00
|
|
|
}
|
|
|
|
|
2019-11-18 23:01:20 +00:00
|
|
|
// Serialize into an AuthenticateResponse which can be sent via Capnp
|
2019-11-20 18:12:08 +00:00
|
|
|
func (ao AuthSuccess) Serialize() AuthenticateResponse {
|
2019-11-18 23:01:20 +00:00
|
|
|
return AuthenticateResponse{
|
2019-11-20 18:12:08 +00:00
|
|
|
Jwt: ao.jwt,
|
|
|
|
HoursUntilRefresh: ao.hoursUntilRefresh,
|
2019-11-18 23:01:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-20 18:12:08 +00:00
|
|
|
func (ao AuthSuccess) isAuthOutcome() {}
|
2019-11-18 16:28:18 +00:00
|
|
|
|
|
|
|
// AuthFail means this cloudflared has the wrong auth and should exit.
|
|
|
|
type AuthFail struct {
|
2019-11-20 18:12:08 +00:00
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewAuthFail(err error) AuthFail {
|
|
|
|
return AuthFail{err: err}
|
2019-11-18 16:28:18 +00:00
|
|
|
}
|
|
|
|
|
2019-12-04 17:22:08 +00:00
|
|
|
func (ao AuthFail) Error() string {
|
|
|
|
return ao.err.Error()
|
|
|
|
}
|
|
|
|
|
2019-11-18 23:01:20 +00:00
|
|
|
// Serialize into an AuthenticateResponse which can be sent via Capnp
|
2019-11-20 18:12:08 +00:00
|
|
|
func (ao AuthFail) Serialize() AuthenticateResponse {
|
2019-11-18 23:01:20 +00:00
|
|
|
return AuthenticateResponse{
|
2019-11-20 18:12:08 +00:00
|
|
|
PermanentErr: ao.err.Error(),
|
2019-11-18 23:01:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-20 18:12:08 +00:00
|
|
|
func (ao AuthFail) isAuthOutcome() {}
|
2019-11-18 16:28:18 +00:00
|
|
|
|
|
|
|
// AuthUnknown means the backend couldn't finish checking authentication. Try again later.
|
|
|
|
type AuthUnknown struct {
|
2019-11-20 18:12:08 +00:00
|
|
|
err error
|
|
|
|
hoursUntilRefresh uint8
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewAuthUnknown(err error, hoursUntilRefresh uint8) AuthUnknown {
|
|
|
|
return AuthUnknown{err: err, hoursUntilRefresh: hoursUntilRefresh}
|
2019-11-18 16:28:18 +00:00
|
|
|
}
|
|
|
|
|
2019-12-04 17:22:08 +00:00
|
|
|
func (ao AuthUnknown) Error() string {
|
|
|
|
return ao.err.Error()
|
|
|
|
}
|
|
|
|
|
2019-11-18 16:28:18 +00:00
|
|
|
// RefreshAfter is how long cloudflared should wait before rerunning Authenticate.
|
2019-11-20 18:12:08 +00:00
|
|
|
func (ao AuthUnknown) RefreshAfter() time.Duration {
|
|
|
|
return hoursToTime(ao.hoursUntilRefresh)
|
2019-11-18 16:28:18 +00:00
|
|
|
}
|
|
|
|
|
2019-11-18 23:01:20 +00:00
|
|
|
// Serialize into an AuthenticateResponse which can be sent via Capnp
|
2019-11-20 18:12:08 +00:00
|
|
|
func (ao AuthUnknown) Serialize() AuthenticateResponse {
|
2019-11-18 23:01:20 +00:00
|
|
|
return AuthenticateResponse{
|
2019-11-20 18:12:08 +00:00
|
|
|
RetryableErr: ao.err.Error(),
|
|
|
|
HoursUntilRefresh: ao.hoursUntilRefresh,
|
2019-11-18 23:01:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-20 18:12:08 +00:00
|
|
|
func (ao AuthUnknown) isAuthOutcome() {}
|
2019-11-18 16:28:18 +00:00
|
|
|
|
|
|
|
func hoursToTime(hours uint8) time.Duration {
|
|
|
|
return time.Duration(hours) * time.Hour
|
2019-12-04 17:22:08 +00:00
|
|
|
}
|