auth_outcome.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package pogs
  2. import (
  3. "errors"
  4. "time"
  5. )
  6. // AuthenticateResponse is the serialized response from the Authenticate RPC.
  7. // It's a 1:1 representation of the capnp message, so it's not very useful for programmers.
  8. // Instead, you should call the `Outcome()` method to get a programmer-friendly sum type, with one
  9. // case for each possible outcome.
  10. type AuthenticateResponse struct {
  11. PermanentErr string
  12. RetryableErr string
  13. Jwt []byte
  14. HoursUntilRefresh uint8
  15. }
  16. // Outcome turns the deserialized response of Authenticate into a programmer-friendly sum type.
  17. func (ar AuthenticateResponse) Outcome() AuthOutcome {
  18. // If the user's authentication was unsuccessful, the server will return an error explaining why.
  19. // cloudflared should fatal with this error.
  20. if ar.PermanentErr != "" {
  21. return NewAuthFail(errors.New(ar.PermanentErr))
  22. }
  23. // If there was a network error, then cloudflared should retry later,
  24. // because origintunneld couldn't prove whether auth was correct or not.
  25. if ar.RetryableErr != "" {
  26. return NewAuthUnknown(errors.New(ar.RetryableErr), ar.HoursUntilRefresh)
  27. }
  28. // If auth succeeded, return the token and refresh it when instructed.
  29. if len(ar.Jwt) > 0 {
  30. return NewAuthSuccess(ar.Jwt, ar.HoursUntilRefresh)
  31. }
  32. // Otherwise the state got messed up.
  33. return nil
  34. }
  35. // AuthOutcome is a programmer-friendly sum type denoting the possible outcomes of Authenticate.
  36. //go-sumtype:decl AuthOutcome
  37. type AuthOutcome interface {
  38. isAuthOutcome()
  39. // Serialize into an AuthenticateResponse which can be sent via Capnp
  40. Serialize() AuthenticateResponse
  41. }
  42. // AuthSuccess means the backend successfully authenticated this cloudflared.
  43. type AuthSuccess struct {
  44. jwt []byte
  45. hoursUntilRefresh uint8
  46. }
  47. func NewAuthSuccess(jwt []byte, hoursUntilRefresh uint8) AuthSuccess {
  48. return AuthSuccess{jwt: jwt, hoursUntilRefresh: hoursUntilRefresh}
  49. }
  50. func (ao AuthSuccess) JWT() []byte {
  51. return ao.jwt
  52. }
  53. // RefreshAfter is how long cloudflared should wait before rerunning Authenticate.
  54. func (ao AuthSuccess) RefreshAfter() time.Duration {
  55. return hoursToTime(ao.hoursUntilRefresh)
  56. }
  57. // Serialize into an AuthenticateResponse which can be sent via Capnp
  58. func (ao AuthSuccess) Serialize() AuthenticateResponse {
  59. return AuthenticateResponse{
  60. Jwt: ao.jwt,
  61. HoursUntilRefresh: ao.hoursUntilRefresh,
  62. }
  63. }
  64. func (ao AuthSuccess) isAuthOutcome() {}
  65. // AuthFail means this cloudflared has the wrong auth and should exit.
  66. type AuthFail struct {
  67. err error
  68. }
  69. func NewAuthFail(err error) AuthFail {
  70. return AuthFail{err: err}
  71. }
  72. func (ao AuthFail) Error() string {
  73. return ao.err.Error()
  74. }
  75. // Serialize into an AuthenticateResponse which can be sent via Capnp
  76. func (ao AuthFail) Serialize() AuthenticateResponse {
  77. return AuthenticateResponse{
  78. PermanentErr: ao.err.Error(),
  79. }
  80. }
  81. func (ao AuthFail) isAuthOutcome() {}
  82. // AuthUnknown means the backend couldn't finish checking authentication. Try again later.
  83. type AuthUnknown struct {
  84. err error
  85. hoursUntilRefresh uint8
  86. }
  87. func NewAuthUnknown(err error, hoursUntilRefresh uint8) AuthUnknown {
  88. return AuthUnknown{err: err, hoursUntilRefresh: hoursUntilRefresh}
  89. }
  90. func (ao AuthUnknown) Error() string {
  91. return ao.err.Error()
  92. }
  93. // RefreshAfter is how long cloudflared should wait before rerunning Authenticate.
  94. func (ao AuthUnknown) RefreshAfter() time.Duration {
  95. return hoursToTime(ao.hoursUntilRefresh)
  96. }
  97. // Serialize into an AuthenticateResponse which can be sent via Capnp
  98. func (ao AuthUnknown) Serialize() AuthenticateResponse {
  99. return AuthenticateResponse{
  100. RetryableErr: ao.err.Error(),
  101. HoursUntilRefresh: ao.hoursUntilRefresh,
  102. }
  103. }
  104. func (ao AuthUnknown) isAuthOutcome() {}
  105. func hoursToTime(hours uint8) time.Duration {
  106. return time.Duration(hours) * time.Hour
  107. }