C# Verständnisproblem Openiddict

DrCox1911

Lt. Junior Grade
Registriert
Juni 2018
Beiträge
503
Nabend zusammen,

ich habe da ein kleines Verständnisproblem bezüglich OAuth2/OpenID Connect in Verbindung mit der .Net Core Lib Openiddict.
Hat hier schon einmal wer von euch damit gearbeitet?

Bisher habe ich einen JWT-basierten Server selber geschrieben, der sich jetzt nicht an die OAuth2 oder auch an die OpenID Connect Spec gehalten hat. Jetzt wollte ich mir einmal einen Spec-Konformen Server schreiben und dafür Openiddict nutzen.

Was ich nicht so ganz verstehe:
Ich will hiermit ja einen SSO-Server schaffen, damit ich andere APIs darüber absichern kann. Also ich habe mehrere User, die einen Client (z.B. eine Web-App mit Razorpages) bedienen. Dieser Client baut auf einer API auf. Damit ich jetzt die API-Endpoints entsprechend absichern kann, also User x am Client ist Admin, User y am Client ist normaler User.

Dafür stellt der Client beim Einloggen der User eine Verbindung zum Openiddict Server her und holt sich ein Token. Mit diesem Token geht er dann zur API und übergibt es dieser bei jedem Request.

Und jetzt mein Problem: Wie überprüft jetzt die API, dass das Token valide ist und auf welche Endpoints damit zugegriffen werden kann?

Hoffe mein Problem ist einigermaßen verständlich.
 
Hi!

Ich kenne Openiddict nicht speziell, aber Identitiy Server und wenn es OIDC ist musst es ja identisch sein.
DrCox1911 schrieb:
Und jetzt mein Problem: Wie überprüft jetzt die API, dass das Token valide ist und auf welche Endpoints damit zugegriffen werden kann?

Das macht die Middleware automatisch, wenn du von der well-known Configuration die Parameter fetched. Zustätzlich muss man die Policy für die richtigen User entsprechend definieren und die Claims beim Callback korrekt übertragen/setzen. Code-Beispiel kann ich für OIDC gerne liefern, falls notwendig.

Ob du am Application Backend dann ein Cookie oder ein JWT ausstellst spielt für die Middleware keine Rolle.

Am Controller validierst du das Cookie/JWT per [Authorize] Annotation, entsprechend natürlich mit der zuvor definierten Policy.

LG Stefan
 
Danke für die Antwort, @MrFlip0815

Code-Beispiel nehme ich gerne, ich tue mich etwas hart, da ich bisher mit JWT immer nur zu tun hatte, wenn die API auch gleichzeitig selber der Aussteller des Tokens war.

Jetzt kommt das Token ja von einem anderen Server und sichert die eigentliche API nur ab.
 
DrCox1911 schrieb:
Jetzt kommt das Token ja von einem anderen Server und sichert die eigentliche API nur ab.

Genau, das ist aber grundsätzlich kein Problem, da (jeder) IP eine well-known Configuration anbietet, die man sich abholen kann und zur Validierung des Tokens dient. Ich hoffe, ich vergesse nicht morgen ein paar Projekte zu durchforsten ;)
Ergänzung ()

Aber meanwhile schau dir mal das Beispiel hier an

https://damienbod.com/2019/11/01/user-claims-in-asp-net-core-using-openid-connect-authentication/

Wichtige Parameter:
  • Authority: Url des IP (in deinem Fall wohl openiddict)
  • ClientID und ClientSecret muss man zwischen beiden sharen
  • Die Redirect URLs sind wichtig, nachdem du vmtl. Authorization Code Flow verwendest?
    • (von einer Website redirected man auf den Identitiy Provider und kommt nach erfolgreichem Login zurück zu seiner Site)
    • ResponseType: Code
  • Scopes: Wichtig für den inhalt des IdToken des IP
  • options.SignInScheme = "Cookies";
    • Man kann natürlich auch ein JWT ausstellen lassen, Cookies sind aber auch ok, dann muss man das JWT storen am Browser nicht selber handeln und Token Revocation ist auch nicht trivial, wenn mans richtig machen will.

Das Beispiel ist jetzt relativ vereinfacht, am Controller verwendet man entsprechend [Authorize], das erledigt die Middleware dann von selbst und man kann aus dem Controller Context die Claims entsprechend auslesen.

in .AddOpenIdConnect kann man noch einige Callbacks registrieren, die wichtig sind um gewisse Steps zu automatisieren. Zum Beispiel könnte man gewisse claims selber dann ins Cookie übertragen, die sich aus dem IdToken des Identity Providers nicht ergeben (Admin User zb). Alternativ könnte man beispielsweise auch gleich einen neuen user, der das erste mal ankommt, in die Application-Datenbank aufnehmen um gewisse dinge vorzubereiten.

C#:
.AddOpenIdConnect("oidc", options =>
            {
                // ...
                // Parameter Siehe Beispiel des Links
              
                options.Events = new OpenIdConnectEvents()
                {
                    OnAuthenticationFailed = context =>
                    {
                       // handle
                    },
                    OnTokenValidated = async context =>
                    {
                        // parse claims from idtoken that is returned from IP
                        string oid = context.Principal.FindFirstValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier");
                        string surName = context.Principal.FindFirstValue("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname");
                      
                        try
                        {
                            Guid.TryParse(oid, out Guid oidGuid);
                            
                            var claims = new List<Claim>
                            {
                                new Claim(TMHClaims.CLAIM_ID_TOKEN, context.ProtocolMessage.IdToken), // we have to keep the id_token and sid for sso-signout later on
                                new Claim(TMHClaims.CLAIM_TMH_NAMEIDENTIFIER, userDto.Id.ToString()),
                                new Claim(TMHClaims.CLAIM_TMH_SID, storedSid)
                            };

                            if (surName.Contains("admin")) // stupid example, but you get the idea.
                            {
                                _logger.LogInformation($"user {surName} logged in as admin");
                                claims.Add(new Claim(ClaimTypes.Role, "siteAdmin"));
                            }
                            // admin is also normal user
                            claims.Add(new Claim(ClaimTypes.Role, TMHRoles.ROLE_USER));

                            var appIdentity = new ClaimsIdentity(claims);
                            context.Principal.AddIdentity(appIdentity);
                        }
                        catch (Exception e)
                        {
                            // handle
                        }
                    },

                    OnAuthorizationCodeReceived = (context) =>
                    {
                        // handle
                        return Task.CompletedTask;
                    },
                    OnRedirectToIdentityProvider = (context) =>
                    {
                        // handle
                        return Task.CompletedTask;
                    },
                    OnRedirectToIdentityProviderForSignOut = (context) =>
                    {
                           // handle
                        return Task.CompletedTask;
                    },
                };
            });
        }
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: DrCox1911
Eventuell habe ich OAuth2/OpenID Connect auch falsch verstanden, daher beschreibe ich besser einmal, was ich denn eigentlich versuche zu erreichen:

Ich will einen Authentifizierungsserver, bei dem andere APIs sich eintragen können. Diese APIs werden ab diesem Zeitpunkt vom Authentifizierungsserver abgesichert.

Ich erkläre das mal an einem Beispiel:
Ich habe eine API für Wetterdaten, darin können sich beliebig viele Wetterstationen befinden. Die API hat dabei drei Rollen, Admin, Read und Write.

Diese Wetter-API soll nun über einen globalen Auth-Server abgesichert werden, sodass entsprechend ein Read-User nur Wetterstation abfragen und ein Write-User neue Wetterstationen hinzufügen kann. Der Admin-User besitzt dann beide Berechtigungen.

Jetzt wird von einer fremden Person ein Client für diese Wetterstation-API geschrieben. Dafür bekommt die Person vom Betreiber der Wetterstation-API nur die Login-Daten für einen Read-User mitgeteilt.
Der Wetterstation-Client stellt also eine Anfrage an den globalen Auth-Server mit diesen Read-Userdaten und erhält ein Token zurück. Mit diesem Token geht er nun auf die Wetterstation-API zu und diese weiß nun anhand vom Token, dass der Endpoint GetAllWeatherStations erlaubt, aber der Endpoint AddNewWeatherStation verboten ist.

Hoffe, damit ist mein gewollter Zielzustand klarer.
In meiner Vorstellung hat der Client dabei auch gar nichts mit OpenIddict zu tun, der weiß davon gar nichts und braucht entsprechend auch nicht die Pakete (kann ja auch sein, dass der Client in Programmiersprache XY geschrieben wird).

Nur was braucht die API von OpenIddict, damit das funktioniert? Bei allen Beispielen bisher, die ich so gesehen habe, ist die Resource-API direkt immer mit der Auth-API in einem Projekt.

Was den Sachverhalt mMn. gut erkärt ist diese Seite hier, da bin ich erstmal speziell an dem Client Credentials Workflow interessiert, da in meinem Fall der eigentliche User der Client App sich nicht einloggen soll, sondern die Client App die Login-Daten sozusagen "fest" im Sourcecode hat.
 
Zurück
Oben