<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://christiaangoossens.nl/feed.xml</id>
    <title>Christiaan Goossens</title>
    <updated>2026-09-11T09:54:00.154Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <author>
        <name>Christiaan Goossens</name>
        <email>contact@christiaangoossens.nl</email>
        <uri>https://christiaangoossens.nl/</uri>
    </author>
    <link rel="alternate" href="https://christiaangoossens.nl/"/>
    <link rel="self" href="https://christiaangoossens.nl/feed.xml"/>
    <logo>https://christiaangoossens.nl/icon.png</logo>
    <icon>https://christiaangoossens.nl/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[CloudCat has closed down.]]></title>
        <id>https://christiaangoossens.nl/notes/cloudcat-closed-down</id>
        <link href="https://christiaangoossens.nl/notes/cloudcat-closed-down"/>
        <updated>2024-11-13T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<span class="lead"><p>On the 1st of November 2024, CloudCat has closed down. After 11 years of webhosting, webdesign and technical support, it&#x27;s time to shift focus.</p></span>
<p>CloudCat never was a big player, but it reflected a passion for the web. I worked<sup><a href="#user-content-fn-1" id="user-content-fnref-1" data-footnote-ref="true" aria-describedby="footnote-label">1</a></sup> to make sure that CloudCat offered what I would have wanted to purchase: webhosting done the modern way, fully secure and managed, fully ready for the future.</p>
<p>CloudCat offered a mix rarely seen in other providers: managed webhosting, with standard DNSSEC/TLSA/DANE for your domains, DMARC/DKIM/SPF for your mail and IPv6, TLS 1.3, HSTS and HTTP/2 for your site. It likely was the most modern webhosting you could buy.</p>
<h2>The shadow side</h2>
<p>All of this had a downside: <strong>cost</strong>, mostly paid in hours.</p>
<p>Default panels like DirectAdmin, Plesk and CPanel come from the early days of the web and are built on scripts accumulated over the years. I had to do it the manual way, manually updating software, configuring every site&#x27;s DNS to have all of these nice standards enabled and manually registering and transferring domains.</p>
<p>Besides, I also supported many different kinds of sites and workspaces, such as both Google Workspace and Microsoft 365, as well as Python, PHP and Node.JS custom apps and pre-built applications such as <a href="https://directus.io/">Directus</a> and <a href="https://nextcloud.com/">Nextcloud</a>.</p>
<p>While I did that with passion for a long time, it wasn&#x27;t sustainable, especially if I wanted to grow the customer base to make it profitable.</p>
<h2>Options</h2>
<p>Together with a friend<sup><a href="#user-content-fn-2" id="user-content-fnref-2" data-footnote-ref="true" aria-describedby="footnote-label">2</a></sup>, I did some market research and investigated the options: was there enough interest in modern webhosting to continue?</p>
<p>The answer, sadly, was <strong>no</strong>. While everyone wants their webhosting to work well, few are willing to pay for it. The market is dominated by big players, competing on cost and getting acquired to create even larger blocks. The Dutch webhosting space is dominated by Your.Online and team.blue, who both raise prices frequently<sup><a href="#user-content-fn-3" id="user-content-fnref-3" data-footnote-ref="true" aria-describedby="footnote-label">3</a></sup>. There certainly was space to be a price-fighter, but not to ask a little more for technically superior hosting.</p>
<h2>Conclusions</h2>
<p>That answer didn&#x27;t fit me. I kept the prices low (with prices between €4,50 and €13,- per month per customer), offering unlimited disk space and bandwidth and only differenciating on support. Lowering the prices to compete wouldn&#x27;t work with the manual time I had to put in and raising it to cover costs of creating a technically advanced automated solution would make it too expensive for the market.</p>
<p>Therefore, I concluded that it was better to close down CloudCat for now. All customers got an individual email with advice on where to go next and a free transfer to a webhost of choice. After this was completed, the servers went offline.</p>
<h2>Interested in webhosting?</h2>
<p>If you are interested in webhosting, I can recommend going to Cloud86, which was my migration partner of choice. Cloud86 is an independent Dutch webhost, not part of any blocks, with reasonable pricing. They support most of the technical features using the Plesk Obsidian panel, which is the most modern of the three panels. They can assist you with configuring DMARC/DKIM/SPF, HSTS and HTTP/2, but they do not yet offer IPv6. For DNSSEC, you will need to email their helpdesk at <a href="mailto:support@cloud86.io">support@cloud86.io</a>.</p>
<p>You can sign up for Cloud86 using the button below (don&#x27;t worry, you can look around first) or by mentioning my affiliate code <code>66f93a9cd2cb7</code> to the helpdesk on signup.</p>
<a href="https://cloud86.io/#a_aid=66f93a9cd2cb7&amp;chan=chrg">Go to the Cloud86 website to view their offer</a>
<section data-footnotes="true" class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>Thank you Evelien for your volunteer help starting CloudCat, creating the visual identity and joining me for some of the webdesign. <a href="#user-content-fnref-1" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-2">
<p>Thank you Ezra for your volunteer help with the market research. <a href="#user-content-fnref-2" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref">↩</a></p>
</li>
<li id="user-content-fn-3">
<p>As can easily be confirmed by filtering Tweakers news on any of the companies in the blocks, such as <a href="https://tweakers.net/zoeken/?keyword=versio">Versio</a>, <a href="https://tweakers.net/zoeken/?keyword=neostrada">Neostrada</a> or <a href="https://tweakers.net/zoeken/?keyword=TransIP">TransIP</a>. <a href="#user-content-fnref-3" data-footnote-backref="" aria-label="Back to reference 3" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content>
        <author>
            <name>Christiaan Goossens</name>
            <email>contact@christiaangoossens.nl</email>
            <uri>https://christiaangoossens.nl/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Not so easy: OAuth2 for first-parties, part 2]]></title>
        <id>https://christiaangoossens.nl/notes/2018-07-26-not-so-easy-oauth2-for-first-parties-part-2</id>
        <link href="https://christiaangoossens.nl/notes/2018-07-26-not-so-easy-oauth2-for-first-parties-part-2"/>
        <updated>2018-07-26T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<p>As described in <a href="/notes/2018-07-25-modern-first-party-auth">my previous post in this series</a>, I was searching for a sign-in solution with the following attributes:</p>
<ul>
<li><strong>Stateless</strong>: I want my authentication and authorization service to handle everything from identity to ACLs and permissions itself. A resource server should only have to check the validity of the token and then read the permissions out of the token. No back-checking requests necesssary.</li>
<li><strong>Preventing code duplication</strong>: I want only one place to change the authentication methods: therefore a change in an upstream auth provider (social login) should not affect my clients written to authenticate to my auth server.</li>
<li><strong>Working on both the web and in mobile clients</strong>: I had to come up with a solution that worked on both the web, and inside a mobile client.</li>
<li><strong>Working with external auth providers, instead of just passwords</strong>: It had to work well with external providers in extension to username &amp; password. Requirements could change at any time, and thus I didn&#x27;t want to lock a certain client to a certain method. Enabling methods for some clients should be as easy as flipping a switch.</li>
</ul>
<p>Again, as described in the previous post, OAuth2 (with some authentication extensions not described here) would work well. Because all clients will either use the Authorization Code or Implicit Grant, changing upstream auth would be as easy as just changing the /authorize endpoint: no client change necessary.</p>
<p>OAuth2 also works well on both the web, as with native clients (using PCKE and for instance Chrome Browser Tabs instead of Webview). It has also been tested there, and is used in practice for third-party apps all the time.</p>
<h3>So, what&#x27;s the problem?</h3>
<p>Well, OAuth2 was designed for third-parties. It&#x27;s often implemented as an additional method for coupling your API to external apps, besides the normal session and cookie based login method for your own apps. In most implementations I found, the internal apps are also still stateful, requesting user permissions with every request, based on the session, instead of using a stateless method like JWTs.</p>
<p>As far as I can see, using OAuth2 for first-party apps as well (besides the Resource Owner Credentials Grant) is not very popular, and thus server libraries are not well adapted to this usecase. I encountered some issues, which I will describe below:</p>
<h3>My issues</h3>
<p>I was using <a href="https://github.com/thephpleague/oauth2-server">thephpleague/oauth2-server</a> to implement my OAuth2 server. It&#x27;s a great library and you should give it a try if you are using PHP! It allows great freedom in implementing how you want to handle both tokens and scopes, but it&#x27;s still secure by default.</p>
<p>For my use-case I needed to change the following things from the default:</p>
<ul>
<li><strong>Permissions and Scopes</strong>: As I am using very short-lived access tokens (for instance 10 min exp time) and embedding granted user permissions into the token (to enable the true stateless system described above), I had to change the way scopes were issued.</li>
<li><strong>Adding details about the authentication to the token</strong>: Because I use the token as well for authentication purposes (it&#x27;s specifically used as a <code>proof of authentication</code> because it&#x27;s what you get as an exchange for your password), OAuth2 wasn&#x27;t sufficient. Therefore I needed to add some attributes from the OpenID Connect <code>id_token</code> to my token, specifically some basic user details (for UI) and details about the authentication method, time and security level. My apps could then decide if the authentication was recent enough to assume that the user was present (like OpenID Connect does).</li>
<li><strong>Changing the Auth Code and Refresh Token to convey information between sessions</strong>: To keep track of the permissions granted by the user or about the original authentication event, this information should be passed through those encrypted tokens as well.</li>
</ul>
<h2>Permissions and scopes</h2>
<p>Changing the expiration time on the token was very easy with this library, and changing the <a href="https://oauth2.thephpleague.com/scope-repository-interface/">ScopeRepository</a> to issue the correct scopes depending on the requested scopes and rights for that user in the database was also a piece of cake (except for some small issue where the method wasn&#x27;t called in the right place as the docs were suggesting: <a href="https://github.com/thephpleague/oauth2-server/pull/923">PR</a>).</p>
<p>However, soon some differences between my interpretation of the OAuth2 spec and theirs began to appear. The <a href="https://tools.ietf.org/html/rfc6749#section-6">OAuth2 spec</a> says:</p>
<blockquote>
<p>The requested scope MUST NOT include any scope<br/>
not originally granted by the resource owner, and if omitted is<br/>
treated as equal to the scope originally granted by the<br/>
resource owner.</p>
</blockquote>
<p>Therefore, logically, when refreshing a token with a refresh_token, you cannot obtain more rights than you started with. I interpreted this as:</p>
<blockquote>
<p>The literal string of requested scopes is stored (in my case into the encrypted refresh token) at the first authorize request, and every time this string is compared again to the current permissions in the database.</p>
</blockquote>
<p>They interpreted as:</p>
<blockquote>
<p>After the issuance of a token, the granted scopes are stored into the refresh token, and upon refresh with that token, we&#x27;ll check the database again if all of those scopes can still be granted. One can thus never obtain more scopes than were in the last token.</p>
</blockquote>
<p>Although both interpretations are fine according to the standard, as we both only issue scopes approved by the user, my case also allows the user to approve of scopes that are currently unavailable, and can thus &#x27;reappear&#x27; in a refresh token after not being present in the first access token, while in theirs this is not possible.</p>
<p>An example will make this clear:</p>
<ol>
<li>The client requests the following scopes for the user in this fake app: <code>read_movies</code> and <code>watch_movies</code>.</li>
<li>If the client is a first-party client and the client has authenticated (with a client_secret) the user will automatically approve of those scopes (Administrator pre-consent), if we&#x27;re unsure of this client, the user will be prompted for consent in normal OAuth fashion. The final set of approved scopes is stored on the server.</li>
<li>Because the user is not a paid user yet, they can only read the list of movies, not yet watch them. Therefore an access_token with <code>read_movies</code> is issued.</li>
<li>During this session the user becomes a paid user. Therefore the client will refresh the token to obtain the new scopes (and it will thus request <code>read_movies</code> and <code>watch_movies</code> again).</li>
<li>In my interpretation, a new token with <code>read_movies</code> and <code>watch_movies</code> will be issued, as <code>watch_movies</code> was approved, just not granted. In their interpretation the client is required to logout the user, and have the user sign-in again to obtain the new scope.</li>
</ol>
<p>For third-parties their interpretation makes sense, because this problem doesn&#x27;t really matter. Users are forgetfull, and may forget that they have even approved that, and logging in again with the external party may not be as much of a hassle.</p>
<p>However, when using this for a first-party app, it&#x27;s pretty strange that the user has to login again to get new permissions. Approval is done without user involvement (just like it is for administrator approved third party apps in normal applications of OAuth2), so for a user this is just weird UX.</p>
<p>Both interpretations adhere to the standard, and they should be equally safe. In both systems if the client requests the <code>delete_movies</code> scope in the refresh request, after it wasn&#x27;t in the original request (see step 1), it will NOT be issued, even if the user is capable of this. Therefore, privilege escallation is not possible. It&#x27;s also possible to restrict clients in their scopes with both systems: if a client cannot request some scopes, they can always be removed in step 1, before comparing the scopes with the database permissions for that user. In this way, some clients may never request certain scopes, even if the user is capable of those things.</p>
<p>This issue resulted in an <a href="https://github.com/thephpleague/oauth2-server/issues/895">issue</a> on the thephpleague/oauth2-server library, because I wanted some input on how to implement this. <a href="https://github.com/simonhamp">@simonhamp</a> and <a href="https://github.com/Sephster">@Sephster</a> helpfully provided me with some input (although my explanation of my situation may have lead them to believe that my resource server would have any way of knowing when data was unavailable and I may not have stressed enough that I want to be stateless and thus prevent resource server to auth server communication about user permissions).</p>
<p>In my use-case some data may be temporarily unavailable to users. This may be at any point, for any duration of time, and could best be viewed as &#x27;retracting a users right to view the data for a certain amount of time&#x27;. Who can view what is determined by the auth server, as the resource server has no way of knowing this (it&#x27;s not like the data is always unavailable at 9.00 pm for some usergroup, it may be at any moment, for any group).</p>
<p>I thus had to include these rights somewhere into the token. I did this through the scopes, but this didn&#x27;t appear to be very common:</p>
<blockquote>
<p>The most solid reason for this is because I believe the majority of folks use scopes in the way they were intended (this is the first mention I&#x27;ve seen of scopes being used in this way): If you can request them and the resource owner granted them, then it&#x27;s generally assumed that the token you use has them and the refresh token can have them too.</p>
</blockquote>
<p>I can fully understand this stance from the way OAuth2 was intended: a third-party would not expect scopes to be used as permissions, as scopes for third-parties just indicate that a user falls into a certain role.</p>
<p>For first-party apps however, this is the easiest stateless way of decoupling permissions from the resource servers. This was my first introduction to the idea of OAuth2 being used for first-parties was very weird.</p>
<h2>Authentication data</h2>
<p>Another issue I encountered when adapting OAuth2 to first-party applications, was the fact that OAuth2 isn&#x27;t suited for authentication. It lacks details about the user and authentication event.</p>
<p>To add these, I also needed to add that information to the Auth Code, to persist it between sessions. This led to another <a href="https://github.com/thephpleague/oauth2-server/pull/924">PR</a>.</p>
<p>After some local edits to the <code>BearerTokenResponse</code>, <code>RefreshTokenGrant</code>, <code>AccessTokenEntityInterface</code> and <code>AuthCodeGrant</code>, I had finally implemented everything necessary for first-party OAuth, as a user would expect it to work.</p>
<h2>Suggestions?</h2>
<p>This may not be the best way of doing this, although I tried to minimize edits to OAuth, to keep it as secure as possible. If you have any input on this, just send an email to <a href="mailto:contact@christiaangoossens.nl">contact@christiaangoossens.nl</a> with your suggestions.</p>
<p>If you are implementing this as well, keep the following in mind:</p>
<ul>
<li>Your access tokens should be very shortlived, as there is no way to retract permissions between expirations. This may be fine in your usecase, or it may not be acceptible.</li>
<li>Your auth code and refresh tokens should be opague and encrypted, because if the information can be edited, user consent can be faked.</li>
<li>The user should always consent specifically to all permissions, or an administrator should approve of the app (with first-party apps) with sufficient client checks applied. The Authorization Code Flow is strongly recommended.</li>
</ul>]]></content>
        <author>
            <name>Christiaan Goossens</name>
            <email>contact@christiaangoossens.nl</email>
            <uri>https://christiaangoossens.nl/</uri>
        </author>
    </entry>
    <entry>
        <title type="html"><![CDATA[Modern authentication and authorization for first-party clients]]></title>
        <id>https://christiaangoossens.nl/notes/2018-07-25-modern-first-party-auth</id>
        <link href="https://christiaangoossens.nl/notes/2018-07-25-modern-first-party-auth"/>
        <updated>2018-07-25T00:00:00.000Z</updated>
        <content type="html"><![CDATA[<link rel="preload" as="image" href="/images/2028-07-Excuse-Me-What.jpg"/><p><em>This post assumes basic knowledge about Sessions, login methods, State, Cookies and protocols such as OAuth, OpenID Connect and SAML. If any of these are not familiar to you, you may want to read up on them first.</em></p>
<p>During work on some projects lately I have come to ask myself one question: &#x27;Why don&#x27;t we have a clear solution for stateless authentication and authorizaton for first-party clients?&#x27;</p>
<p>The world of authentication and authorization seems so simple:</p>
<ul>
<li>For first-parties we have the trusted (and widely used) sessions and cookies approach for maintaining login sessions, where the permissions are locally determined on the server, often based on some ACL (Access Control List).</li>
<li>For third-parties we have <a href="https://tools.ietf.org/html/rfc6749">OAuth(2)</a> and <a href="http://openid.net/specs/openid-connect-core-1_0.html">OpenID Connect</a>, or <a href="http://saml.xml.org/saml-specifications">SAML</a> which allow secure access to user details and data, and provide amazing benefits, such as being nearly stateless (including JWTs), having a decoupled Authentication Server and Relying Party (which allows for easy updating of the authentication methods, no updating of clients necessary) and more precise control over what permissions are requested (using scopes).</li>
</ul>
<p>This does not cover all use-cases. We have skipped the third-parties using the cookies and sessions approach, which existed before OAuth, and is thus being fased out and deprecated.</p>
<p>However, more importantly, we have also skipped the use-case where we want our first-party app to use the benefits of the OAuth and OpenID Connect system. This post is about that last case, and the my eternal search for the best solution of implementing such a stateless system for both authentication and authorization in first-party apps.</p>
<p><img src="/images/2028-07-Excuse-Me-What.jpg" alt="Excuse-me-What.jpg"/></p>
<p>That was, and still is my (first) reaction to this question. Shouldn&#x27;t we have a clear answer to this question yet? After all this talk of microservices and deconstructing the monolithic applications into small parts, someone should have already solved this. Right?</p>
<h3>First attempt: OAuth2 is unnecessarily complicated for this, I can do better</h3>
<p>As anyone would, I started this journey thinking I could do better, I could write something easier than OAuth2 and OpenID Connect, while still providing all the benefits, but removing all the overhead not necessary for first-party clients.</p>
<p>First, I will provide some context on what I was working on: I was tasked to create a first-party mobile app, which should use <a href="https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-developers-guide">Office 365 (through Azure AD)</a> to allow employees to login. Eventually, they wanted to extend the app to a web-app and the manager should be able to login without using Office 365, and thus using local accounts. After login through Office 365, the user details would be sent back to the server, to be coupled with some other backend system data on that user. All that data combined would then form the user profile, thus also determening user rights.</p>
<p>From this client story, the following requirements emerge:</p>
<ul>
<li>User data from Office 365 should be sent from Microsofts servers, through the mobile app, to our servers, without being modified, because that may influence user permissions.</li>
<li>I had the choice of either using SAML or OpenID Connect in Azure AD, but I would have to integrate the browser on the mobile device anyway, as I was (logically) not allowed to access user credentials for Office 365 directly.</li>
</ul>
<p>My first solution was thus to use OpenID Connect with Azure AD (by integrating Microsofts ADAL library into the app) and have the mobile client send it&#x27;s id token (from Azure AD) to the backend server on some /token endpoint, where it would be exchanged for a JWT (to maintain the statelessness), which the mobile client would use for communicating with our APIs. Our backend server would first verify that id token, and then combine the provided data with our data on that user.</p>
<p>I would implement the Azure AD login into the mobile app first, and then into the web app seperately. The login system for our management system would be seperate to cater to the need of password login (instead of Azure AD login).</p>
<h3>Hmm, that seems, eh.. a bit... repetetory and non-futureproof?</h3>
<p>Yes, yes it is. What would happen if they would have wanted Office 365 login on the management portal anyway? I would have to implement the same Azure AD system there as well (for the third time). What if Azure would make breaking changes to their OpenID Connect implementation? I would have to change it in two (or even three) places. What if they added or removed claims?</p>
<p>Also, I was implementing a token endpoint with the function of converting a JWT bearer (id) token from Azure AD into my own JWT format, and I would be using that same endpoint as well for password authentication from that management app. Essentially, I would have implemented the <a href="https://tools.ietf.org/html/rfc6749#section-4.3">OAuth2 Resource Owner Credentials Flow</a> and the <a href="https://tools.ietf.org/html/rfc7523#section-2.1">OAuth2 JWT Bearer Token Flow</a> anyway.</p>
<p>To summerize I had to build the following in my first model:</p>
<ul>
<li><strong>Token Endpoint</strong> accepting both username &amp; password (like the normal first-party authentication method we all know (and love?)) and the JWT token from Azure AD</li>
<li><strong>Either an implementation of a library (ADAL) or my own solution to handling the authorization process</strong>, such that I could obtain an id token from Azure AD. Because I was using Cordova, and I really wanted to use the secure Chrome Browser Tabs (not the Webview ADAL was using), I was already stuck with the second option.</li>
<li><strong>Custom JWT issuing implementation</strong> to issue my own stateless JWT access tokens after the user succesfully authenticated to the token endpoint.</li>
</ul>
<h3>Second attempt: What about, just using OAuth2 for first-party clients as well?</h3>
<p>After a while it occured to me that I had essentially rebuilt OAuth2, without the implicit grant and auth code grant, but including my own custom JWT implementation. I already already had the code for these grants in most of my clients.</p>
<p>Wasn&#x27;t it better to just use an off the shelf audited OAuth2 Server library then to create all of it yourself? That&#x27;s when I started thinking about a way to do first-party authentication with OAuth2 itself (adding some things to make OAuth2 usable for authentication, for instance OpenID Connect).</p>
<p>I would just use the <a href="https://tools.ietf.org/html/rfc8252#section-6">Auth Code Flow (with PKCE and without client_secret)</a> for the mobile app, just like I would with Azure AD, but this time with my own /authorize endpoint as well, directly obtaining a JWT access token and id token, without the extra call to our API exchanging Azure ADs id token for our tokens. I could also use Implicit Flow for both our management app and the web app.</p>
<p>No need for the Resource Owner Credentials Grant anymore. We also gain SSO (logging in for the webapp can also mean automatic login for the management portal) benefits, because we are using our own /authorize endpoint.</p>
<p>We have also solved the previous issues: if the client wants Office 365 login on the management portal, I will just enable that for the management portal client in my OAuth2 Server implementation and it&#x27;s done. If Azure AD changes, then I just edit my implementation once in the login system for my OAuth2 Server. No need to change all clients, they just keep using the same /authorize and /token endpoints the exact same way, as my JWTs have not changed at all. I even have the benefit of reusing my OAuth2 Server implementation in other projects because it&#x27;s possible to build as a generalized module, where I can make all authentication providers (Office 365, password or even Google and Facebook) function as extensions.</p>
<h3>So, we are done?</h3>
<p>Woah, not so fast! Who said OAuth2 actually works in a first-party usecase? According to the spec (which is deliberately vague), this should work fine. During my actual implementation however, I encountered some fun problems, which I will describe in <a href="/notes/2018-07-26-not-so-easy-oauth2-for-first-parties-part-2">my next post in this series</a>.</p>
<p><em>Some other thing to note is that, as I didn&#x27;t want to provide a way to integrate my login into other apps as well (I don&#x27;t trust my implementation that much over audited servers), I decided not to use OpenID Connect to provide information about the user and authentication, but to include that information into my JWT accesstoken instead. I still provide information about the user and authentication method, time and security in a legiable and signed way, so essentially it can still be used for authentication. Remember, OAuth2 alone is not enough for authentication, as you don&#x27;t get any information about the authentication itself and when it occurred. The user may not be present anymore at all (which is often the case with third-party access).</em></p>
<p><em>You may not need all of OpenID Connect as well, depending on your use-case. You should however strive to implement as much according to the standards anyway, as implementing these sorts of things is always hard, and making mistakes is easy and may lead to data breaches.</em></p>]]></content>
        <author>
            <name>Christiaan Goossens</name>
            <email>contact@christiaangoossens.nl</email>
            <uri>https://christiaangoossens.nl/</uri>
        </author>
    </entry>
</feed>