Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HTTP 401 responses should include WWW-Authentciate header #48

Merged
merged 1 commit into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,25 @@ func (ar *AppRequest) ContentTypeJSON() {
ar.ContentType("application/json")
}

func (ar *AppRequest) SetWwwAuthenticate(challenge, realm, scope string) {
ar.SetHeader(
"WWW-Authenticate",
fmt.Sprintf(`%s realm="%s" scope="%s"`, challenge, realm, scope),
)
}

func (ar *AppRequest) SetWwwAuthScope(scope string) {
ar.SetWwwAuthenticate("Bearer", "suse-telemetry-service", scope)
}

func (ar *AppRequest) SetWwwAuthReauth() {
ar.SetWwwAuthScope("authenticate")
}

func (ar *AppRequest) SetWwwAuthRegister() {
ar.SetWwwAuthScope("register")
}

func (ar *AppRequest) Status(statusCode int) {
ar.Log.Debug("Response status", slog.Int("code", statusCode))
ar.W.WriteHeader(statusCode)
Expand Down
14 changes: 9 additions & 5 deletions app/handler_authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ func (a *App) AuthenticateClient(ar *AppRequest) {
return
}
if caReq.ClientId <= 0 {
ar.ErrorResponse(http.StatusBadRequest, "Invalid ClientId value provided")
ar.SetWwwAuthRegister()
ar.ErrorResponse(http.StatusUnauthorized, "Invalid ClientId value provided")
return
}
ar.Log.Debug("Unmarshaled", slog.Any("caReq", &caReq))
Expand All @@ -45,8 +46,8 @@ func (a *App) AuthenticateClient(ar *AppRequest) {

// confirm that the client has been registered
if !client.Exists() {
// TODO: Set WWW-Authenticate header appropriately, per
// https://www.rfc-editor.org/rfc/rfc9110.html#name-www-authenticate
// client needs to register
ar.SetWwwAuthRegister()
ar.ErrorResponse(http.StatusUnauthorized, "Client not registered")
return
}
Expand All @@ -59,12 +60,15 @@ func (a *App) AuthenticateClient(ar *AppRequest) {
slog.String("Req Hash", caReq.InstIdHash.String()),
slog.String("DB Hash", instIdHash.String()),
)
// TODO: Set WWW-Authenticate header appropriately, per
// https://www.rfc-editor.org/rfc/rfc9110.html#name-www-authenticate
// client needs to re-register
ar.SetWwwAuthRegister()
ar.ErrorResponse(http.StatusUnauthorized, "ClientInstanceId mismatch")
return
}

// TODO: return existing token if remaining duration is >= half of
// a new tokens duration

// create a new token for the client
client.AuthToken, err = a.AuthManager.CreateToken()
if err != nil {
Expand Down
39 changes: 28 additions & 11 deletions app/handler_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,24 @@ import (
func (a *App) ReportTelemetry(ar *AppRequest) {
ar.Log.Info("Processing")

// verify that a valid authtoken has been provided
// retrieve required headers
hdrClientId := ar.GetClientId()
token := ar.GetAuthToken()

// missing clientId or token suggests client needs to register
if (hdrClientId == "") || (token == "") {
// client needs to register
ar.SetWwwAuthRegister()
ar.ErrorResponse(http.StatusUnauthorized, "Client registration required")
return
}

// verify that a valid authtoken has been provided
if err := a.AuthManager.VerifyToken(token); err != nil {
// TODO: Set WWW-Authenticate header appropriately, per
// https://www.rfc-editor.org/rfc/rfc9110.html#name-www-authenticate
ar.ErrorResponse(http.StatusUnauthorized, "Missing or Invalid Authorization")
// client needs to re-authenticate
ar.SetWwwAuthReauth()
ar.ErrorResponse(http.StatusUnauthorized, "Invalid Authorization")
return
}

ar.Log.Debug(
Expand All @@ -29,12 +41,12 @@ func (a *App) ReportTelemetry(ar *AppRequest) {
)

// verify that the provided client id is a valid number
hdrClientId := ar.GetClientId()
clientId, err := strconv.ParseInt(hdrClientId, 0, 64)
if err != nil {
// TODO: Set WWW-Authenticate header appropriately, per
// https://www.rfc-editor.org/rfc/rfc9110.html#name-www-authenticate
// client needs to register
ar.SetWwwAuthRegister()
ar.ErrorResponse(http.StatusUnauthorized, "Invalid Client Id")
return
}

// verify that the request is from a registered client
Expand All @@ -45,17 +57,22 @@ func (a *App) ReportTelemetry(ar *AppRequest) {
ar.ErrorResponse(http.StatusInternalServerError, "failed to access DB")
return
}

if !client.Exists() {
// TODO: Set WWW-Authenticate header appropriately, per
// https://www.rfc-editor.org/rfc/rfc9110.html#name-www-authenticate
// client needs to register
ar.SetWwwAuthRegister()
ar.ErrorResponse(http.StatusUnauthorized, "Invalid Client Id")
return
}

// verify that the provided authtoken matches last authtoken issued to the client
if client.AuthToken != token {
// TODO: Set WWW-Authenticate header appropriately, per
// https://www.rfc-editor.org/rfc/rfc9110.html#name-www-authenticate
// TODO detect cloned clients, where InstID matches ClientId, but authtoken will
// will be stale
// client needs to re-authenticate
ar.SetWwwAuthReauth()
ar.ErrorResponse(http.StatusUnauthorized, "Invalid Authorization")
return
}

ar.Log.Debug(
Expand Down