missing /authorize endpoint ?
Preflight Checklist
- [X] I could not find a solution in the existing issues, docs, nor discussions
- [X] I have joined the ZITADEL chat
Describe your problem
Read Support for the OpenID Connect(OIDC) Standard in a Custom Login UI https://zitadel.com/docs/guides/integrate/login-ui/oidc-standard
zitadel/typescript repo seems missing /authorize endpoint
so you have to go through ${ZITADEL_URL}/oauth/v2/authorize endpoint
getting authRequestId is puzzling
Let's say I successful generated Zitadel Authorization URL:
http://localhost:8080/oauth/v2/authorize?client_id=285778005274460424%40rachel_rich_and_happy_&nonce=&prompt=none&redirect_uri=http%3A%2F%2Flocalhost%3A5173%2Fzitadel%2Floginname&response_type=code&scope=openid+profile+email&state=
It will put me through Zitadel UI anyway be forcing redirect
Couldnt find a straight forward way getting authRequestId
Is missing /authorize endpoint in this repo?
Describe your ideal solution
implement /authorize endpoint
Version
latest
Environment
Self-hosted
Additional Context
No response
Our general idea here is that the login-ui only "proxies" the request to the api so that the login does not need to take care of the oidc specific stuff.
I think the zitadel/typescript should already be able to proxy the authorize path.
Have you checked what happens when you call ${LOGIN_URL}/oauth/v2/authorize
Might relate to this https://github.com/zitadel/typescript/issues/46
Our general idea here is that the login-ui only "proxies" the request to the api so that the login does not need to take care of the oidc specific stuff.
@fforootd thank you for making it clear. really appreciate it
i ended up doing proxing similar to
https://github.com/zitadel/typescript/blob/main/apps/login/src/middleware.ts
export const config = {
matcher: [
"/.well-known/:path*",
"/oauth/:path*",
"/oidc/:path*",
"/idps/callback/:path*",
],
};
but the ideal situation for me is to get rid of all proxying in favor of backend api calls
i use this repo mainly as an inspiration
what i'm trying to archive
- want to use golang on backend ... cause terrible experience with nextjs
- want to reduce frontend footprints to the minimum - dont want to load two UIs one is mine(hosted) and another from zitadel
- zitadel/typescript repo relies on protobuf generated lib and its bandle size is bloated from my perspective
- dont want endusers to stack in zitadel ui ... zitadel as a backend service is ok. zitadel ui for admin purpose is really great
i might be moving into a wrong direction and eventually will end up with lot of hacking or some compromises
as for now i'm facing issues like on screenshot below - the enduser stack in zitadel ui, not inside my hosted app
and the point is that proxing is not really great user experience ... at least for me ... if you have well defined api and an example how to customize it
and do something similar to
func (h *Handler) RegisterConnections(api huma.API) {
huma.Register(api,
huma.Operation{
Path: "/oauth2/callback",
OperationID: "oauth2-client-callback",
Description: "Serve OAuth2 client callback",
Method: http.MethodPost,
Tags: []string{"oauth2-auth"},
DefaultStatus: http.StatusOK,
}, h.callback)
}
func (h *Handler) callback(ctx context.Context, in *CallbackIn) (out *CallbackOut, err error) {
tx := query.New(h.dbp)
stateUUID := uuid.FromStringOrNil(in.State)
if stateUUID.IsNil() {
h.log.Error("broken state parameter", "error", err)
return nil, huma.Error400BadRequest("broken state parameter")
}
state, err := tx.GetOauth2State(ctx, stateUUID)
if err != nil {
h.log.Error("query state object", "error", err)
return nil, huma.Error401Unauthorized("query state object")
}
...
to bring the standard endpoints into hosted app by triggering apis calls
Can you exapand what you mean with "but the ideal situation for me is to get rid of all proxying in favor of backend api calls"
Generally speaking the proxy piece is only needed if you want to serve OIDC/OAuth from your login to your apps.
With what stack are you building your login?
@fforootd
With what stack are you building your login? front-end ant design (rewriting UI components is the easiest part for me) back-end golang, postgres
Can you expand what you mean with "but the ideal situation for me is to get rid of all proxying in favor of backend api calls"
i thought it would be possible to do embeded callbacks or customize endpoints similar to router on ui/login
https://github.com/zitadel/zitadel/blob/main/internal/api/ui/login/router.go
EndpointExternalLogin = "/login/externalidp" EndpointExternalLoginCallback = "/login/externalidp/callback" EndpointExternalLoginCallbackFormPost = "/login/externalidp/callback/form"
i'm a half way through reading external_provider_handler.go https://github.com/zitadel/zitadel/blob/main/internal/api/ui/login/external_provider_handler.go
and LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)), https://github.com/zitadel/zitadel/blob/main/internal/api/oidc/server.go
and it's look like it's impossible to replicate by using SDK zitadel/zitadel-go sdk as well, cause not all methods which are used in external_provider_handler.go are exposed or mean to be used outside of ui/login
the goal of all the above for now is to understand if i can
- hide zitadel ui and backend from outside visitors ... as its always great to reduce attack surface
- reduce the amount of javascript code exposed to enduser
- if you do embeded login, there should be no situation where enduser will hit unknown UI with duplicated functionality
- /ui/login code has lot of places with "l.renderError(w, r, authReq, err)"
I'm looking for "api-first" solution for embeded login
Here's an example of function I can't trigger using zitadel-go ... or might miss something
https://github.com/zitadel/zitadel/blob/main/internal/api/oidc/auth_request.go#L92 func (o *OPStorage) createAuthRequestLoginClient(ctx context.Context, req *oidc.AuthRequest, hintUserID, loginClient string) (op.AuthRequest, error) { func (c *Commands) AddAuthRequest(ctx context.Context, authRequest *AuthRequest)
https://github.com/zitadel/zitadel/blob/main/internal/api/grpc/oidc/v2/oidc.go
Again i'm a half way through reading ... any hints are greatly appreciated
I see.
Let me try and explain our approach here.
Our current mental model is to seperate the login from the api in a way that the login ui (whatever language it uses) only needs to implement the user facing flows. Like login, reset, register,.... At the same time we want to keep all the business logic for the OIDC/OAuth/SAML in the API and allow the UI to just call the API as a proxy.
It is also possible to call that api without proxing through the login ui, but we thought in many cases you end up having all this on the same domain behind a proxy where you can directly send the traffic to the api so the proxy in the login becomes obsolete.
I think im looking for the same setup as reactima. I want the frontend custom ui to ONLY use endpoints in my own backend. So the backend will act like a proxy for some of the requests Frontend -> Backend -> Zitadel
So by API first you can actually do the whole registrationa and login by using API endpoints of your backend.
Im currently reading up on the issue to check if others are using the same setup and how to handle security like PKCE and such.
@larsbloch not sure if it will help. it's almost impossible to hide all routes without breaking things.
i get a proof of concept by patching few ui/login routes at some point, and discovered it breaks too many things and puts you on the path of maintaining too many routes
have a look here https://github.com/zitadel/zitadel/tree/main/internal/api/ui/login
l.renderer.RenderTemplate or renderError - templates are hardcoded
my own conclusion OIDC spec wasn't designed with API first mentality, and mostly serves IDP providers interest ... and i'm totally fine with it.
zitadel team did awesome job - you can customize ui templates from admin ui.
another drawback of deep customization - you will end reading the entire codebase source code ... i did it for pleasure but all my goland devs friends who saw it had their sole crashed
especially in the eventstore https://github.com/zitadel/zitadel/tree/main/internal/eventstore
p.s. happy to chat with whoever want to go the hard way with zitadel ... and hope one day zitadel's big customers will sponsor true "api first" functionality
@fforootd thank you for the above explanation. it makes more sense for me now
Closing this due to inactivity.