Skip to content

Conversation

@mqf20
Copy link
Contributor

@mqf20 mqf20 commented Aug 10, 2025

Resolves #763

Definition of Ready

  • I am happy with the code
  • Short description of the feature/issue is added in the pr description
  • PR is linked to the corresponding user story
  • Acceptance criteria are met
  • All open todos and follow ups are defined in a new ticket and justified
  • Deviations from the acceptance criteria and design are agreed with the PO and documented.
  • No debug or dead code
  • My code has no repetitions
  • Critical parts are tested automatically
  • Where possible E2E tests are implemented
  • Documentation/examples are up-to-date
  • All non-functional requirements are met
  • Functionality of the acceptance criteria is checked manually on the dev system.

mqf20 added 30 commits July 23, 2025 00:59
Signed-off-by: mqf20 <[email protected]>
Signed-off-by: mqf20 <[email protected]>
Signed-off-by: mqf20 <[email protected]>
Signed-off-by: mqf20 <[email protected]>
Signed-off-by: mqf20 <[email protected]>
Signed-off-by: mqf20 <[email protected]>
Signed-off-by: mqf20 <[email protected]>
Signed-off-by: mqf20 <[email protected]>
Signed-off-by: mqf20 <[email protected]>
Signed-off-by: mqf20 <[email protected]>
// Accept: application/json
// Host: server.example.com
// Authorization: Bearer this.is.an.access.token.value.ffx83
func getBearerToken(r *http.Request) (string, error) {
Copy link

@zekth zekth Aug 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would accept Authorization: Bearerfoobar 1111
suggestion:

func getBearerToken(r *http.Request) (string, error) {
	authHeader := r.Header.Get("authorization")
	if len(authHeader) == 0 {
		return "", errMissingAuthorizationHeader
	}
	tokenSlice := strings.Split(authHeader[0], " ")
	if tokenSlice[0] != "Bearer" {
		return "", errors.New("unsupported auth method")
	} else if len(tokenSlice) != 2 {
		return "", errInvalidHeader
	}
	return tokenSlice[1], nil
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this would accept Authorization: Bearerfoobar 1111

getBearerToken checks against oidc.PrefixBearer:

	if !strings.HasPrefix(auth, oidc.PrefixBearer) {
		http.Error(w, "invalid header", http.StatusUnauthorized)
		return false, ""
	}

In other words,

# not accepted

Authorization: Bearerfoobar 1111

# accepted (returns `foobar 1111`)

Authorization: Bearer foobar 1111

getBearerToken is modelled after checkToken:

func checkToken(w http.ResponseWriter, r *http.Request) (bool, string) {
	auth := r.Header.Get("authorization")
	if auth == "" {
		http.Error(w, "auth header missing", http.StatusUnauthorized)
		return false, ""
	}
	if !strings.HasPrefix(auth, oidc.PrefixBearer) {
		http.Error(w, "invalid header", http.StatusUnauthorized)
		return false, ""
	}
	return true, strings.TrimPrefix(auth, oidc.PrefixBearer)
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad wasn't explicit enough; i was more meaning about error checking like differenciating between protocols. Bearer Basic and so on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying, I didn't read about Dynamic Client Registration supporting basic auth, is this something we have to support?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No but for example okta uses SSWS, depends if we want to make it generic or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zekth I'm fine if you would like to add to this PR, or submit a follow-up PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would a reviewer like to weigh in on this? @elinashoko

@elinashoko
Copy link

Heya @mqf20 thanks for your contribution - we'll review this as soon as we can, currently working through a lot of contributions.

@elinashoko elinashoko moved this to 📋 Sprint Backlog in Product Management Aug 11, 2025
Copy link

@lukaslihotzki-f lukaslihotzki-f left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach looks good. I especially like the modeling of the JSON schema as Go types (for example, InternationalizedField).

Comment on lines +275 to +291
// AuthorizeClientRegistration will check if a Client Registration Request ([RFC7591]) is authorized by parsing
// either:
//
// - an initial access token (OAuth 2.0 access token optionally issued by an authorization server to a developer
// or client and used to authorize calls to the client registration endpoint.), or
// - a software statement (A digitally signed or MACed JSON Web Token (JWT) [RFC7519] that asserts metadata
// values about the client software.)
//
// If the initial access token is invalid, return an ErrInvalidInitialAccessToken.
//
// If the software statement is invalid, return an ErrInvalidSoftwareStatement.
//
// If the software statement presented is not approved for use by this authorization server, return an
// ErrUnapprovedSoftwareStatement.
//
// [RFC7591]: https://www.rfc-editor.org/rfc/rfc7591
// [RFC7519]: https://www.rfc-editor.org/rfc/rfc7519

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To support open registration and facilitate wider interoperability,
the client registration endpoint SHOULD allow registration requests
with no authorization (which is to say, with no initial access token
in the request). These requests MAY be rate-limited or otherwise
limited to prevent a denial-of-service attack on the client
registration endpoint.

This comment contradicts RFC 7591 (section 3.) by implying Protected Dynamic Client Registration. Implementing Open Dynamic Client Registration should be possible by always returning nil.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lukaslihotzki-f I'm not sure I follow your point - would you like to suggest a fix?

@lukaslihotzki-f
Copy link

This PR only adds the routes in op.go, but not in http_server.go. I don't know why there are two routers with separate handler functions, but Zitadel seems to use http_server.go. Otherwise, when configuring a registration endpoint, Zitadel only returns 404 on the advertised registration endpoint. I used this commit to add the route to http_server.go, which works: famedly@a38fab7 This commit implements the new interface in Zitadel: famedly/zitadel@c747723

Is there a way to use the existing interface (ClientsStorage) in Zitadel? Otherwise, all routes should also be added to http_server.go.

@muhlemmer
Copy link
Collaborator

Please take note of #785

@muhlemmer muhlemmer removed the status in Product Management Aug 19, 2025
@muhlemmer muhlemmer moved this to 📨 Product Backlog in Product Management Aug 19, 2025
@mqf20
Copy link
Contributor Author

mqf20 commented Sep 28, 2025

Please take note of #785

@muhlemmer any chance this PR can be looked at in an upcoming sprint?

Co-authored-by: lukaslihotzki-f <[email protected]>
@mqf20
Copy link
Contributor Author

mqf20 commented Sep 28, 2025

This PR only adds the routes in op.go, but not in http_server.go. I don't know why there are two routers with separate handler functions, but Zitadel seems to use http_server.go. Otherwise, when configuring a registration endpoint, Zitadel only returns 404 on the advertised registration endpoint. I used this commit to add the route to http_server.go, which works: famedly@a38fab7 This commit implements the new interface in Zitadel: famedly/zitadel@c747723

Is there a way to use the existing interface (ClientsStorage) in Zitadel? Otherwise, all routes should also be added to http_server.go.

@lukaslihotzki-f do you mean all routes should be added to server_http.go (instead of http_server.go)?

Also, will you be submitting a PR for your fix?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Gathering community feedback

Development

Successfully merging this pull request may close these issues.

Support setting registration_endpoint metadata via option.

6 participants