typescript icon indicating copy to clipboard operation
typescript copied to clipboard

missing /authorize endpoint ?

Open reactima opened this issue 1 year ago • 9 comments

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

reactima avatar Sep 20 '24 13:09 reactima

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

fforootd avatar Sep 24 '24 11:09 fforootd

Might relate to this https://github.com/zitadel/typescript/issues/46

fforootd avatar Sep 24 '24 11:09 fforootd

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

image

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

reactima avatar Sep 24 '24 14:09 reactima

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 avatar Sep 25 '24 10:09 fforootd

@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

reactima avatar Sep 25 '24 13:09 reactima

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.

fforootd avatar Sep 26 '24 11:09 fforootd

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 avatar Nov 30 '24 18:11 larsbloch

@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

reactima avatar Dec 11 '24 18:12 reactima

@fforootd thank you for the above explanation. it makes more sense for me now

reactima avatar Dec 12 '24 05:12 reactima

Closing this due to inactivity.

peintnermax avatar Oct 27 '25 13:10 peintnermax