jPrime 2026

Practical MCP Security in Action, Willem Jan Glerum

43:59 · 03 Jun 2026 – 04 Jun 2026 · YouTube

About this talk

This talk discusses the integration of AI and security through the Model Context Protocol (MCP). The speaker, Willem-Jan, a software engineer from the Netherlands, explores how to secure AI applications that leverage MCP by establishing appropriate authentication and authorization mechanisms. He explains the challenges of integrating large language models (LLMs) with internal data and the need for structured communication channels. The session covers various authentication methods, including OAuth2 with dynamic client registration and mutual TLS, to ensure that only authorized users and applications can access sensitive data. Additionally, the talk includes practical demonstrations using a Quarkus application that interacts with an MCP server, showcasing features like user permission management and step-up authentication for sensitive actions.

Full transcript

All right. Good morning, everyone. So, this is the first session of today. Welcome Thanks for having me back again. I really enjoyed last year, so I hope this year will be a great addition, too. Who here uses AI in their work? Almost everyone, right? So, this is also a talk with something about AI, but also about security. So, people are going to think, "How can you do

AI and security?" Well, in this talk we'll try and show you how we can secure your services with MCP. Who out there is using MCP in their company? A good bunch. Good. Nice. And who has secured their MCP? Uh one or two hands. All right, so we came to the right place. Uh as Dotan said, my name is Willem-Jan. I'm a software engineer from the Netherlands, based

in Rotterdam, and I live in Delft. I've been using and now sometimes contributing to the Quarkus ecosystem since the beginning. I love to do cloud, open source, automate everything out there, uh and care about quality code and In the weekends, I'll try not to be behind my keyboard, but actually be outside. But as we're all software engineers, you spend your time in the uh fiddling away, preparing

this talk, making some demos. So, it doesn't always work, but I try to go outside as well. So, when talking about AI, we have a lot of AI out there. A lot of things you want the AI to do something useful. So, we get AI assistants or AI agents, and they have some knowledge themselves, especially the the pretty recent models. They have up-to-date knowledge. They can research

the internet themselves, but they probably don't know about you, don't know about your company. Well, if you're a big-ass company, of course they will know. If you're a small company, that that typically your AI won't know your domain, the services, and the data you have. At the same time, you also don't want to expose all the data that you have in your company or from your personal

data to an LLM and just give it out there for free. So, [snorts] we need to put some guardrails in place, and that's what we're going to talk about today. So, MCP. So, we have the model context protocol. That's short and the short hand is MCP. And it's a protocol so the AI client so the AI clients can talk to tools, prompts, and resources. on the right-hand

side you have your stuff. So, maybe your REST API that you have built internally, your database, maybe some private files, and whatever the AI needs. Well, this has been great when we're doing enterprise software stacks. Well, we can integrate that with the classic system integration, and we connect all those systems together. Now, we have an LLM, and it just wants to talk to your data right away.

Well, we need to have some structured way of doing this. That's why they came up with MCP. So, instead of every project defining their own strategies how to integrate with your database, you can build one MCP server to talk to your REST API or to talk to your database, for example, internally. Then you can deploy these. So, either you can have these deployed somewhere centrally so people

can use them, or when you're using a coding agent locally with Claude Code, for example, you can have that MCP server running on your own machine. And then an MCP client, like Claude Code or any coding agent, can then use your MCP server. But also, any application out there can interact with your MCP server. So, it's basically the modern way, the AI way of doing system integration.

Well, when we have classical system integration, we can have API keys. Well, that's not so secure anymore, so we came up with all our for example, uh mutual TLS. Uh that's how we used to do it. With MCP from the beginning, nobody thought about authentication authorization. Just let's use all the tools out there, you can just use them. Let the LLM go crazy. That is of course

not great. Because we need to put some lock in the middle that says who can access what of this data. That we'll be seeing today. So, this is what I mentioned before, the MCP standard shipped with no standard authentication. So, a lot of the the the the people out there came up with their own creative ways how to implement authentication on top of MCP. And it was

a bit of a wild west. Also, if we go back to this picture, uh for example, what I've seen people doing as well, an admin generates an admin key, an admin API key, puts it in the MCP server, and then anyone calling the MCP server can do whatever the admin can do. That's probably not what you want. Then, end of 2025, they updated the spec again, and

then they formally put authentication in the spec. But of course, AI is moving super fast. This was November 2025. There's now already a new draft spec out with updated things on authorization as So, what are your options when you're doing authentication? If you have a local MCP server that just integrates with your local system. We don't really need this, right? We don't need authentication locally. We just

have permissions that an LLM can access certain tools, but it doesn't need permissions, but it can but it can just talk directly over standard input output directly. So, there we don't need any authentication. So, it's trivial to implement. We're done. Then what I mentioned, a static API key. So, if you generate it, maybe for a small use case, can be good enough. Uh but what if someone

leaks that key? Or in mid-transport, the key gets leaked? Then we have to rotate. Well, that's not great. So, the effort is low, auditability is weak, and it's a shared key, and they typically don't want to do this. Then in the middle, we have our OAuth2 standard, uh including PKCE and DCR. We'll be going into the abbreviations a bit later as well today. And there we get

from OAuth, we can have tokens that are short-lived, revocable, uh they're auditable because this token is issued for me or for a service account. So, we can know who did something. And the effort, well, we'll see later, it takes a bit of effort. It's definitely more than implementing an API key, but the tooling around it should be fairly easy. Then we can also use the client credentials

for service accounts, uh but that's more for service-to-service communication. And in our use case, we're going to see more uh where we have real users. Then if we take it up a notch, if you want to secure it even further, and that's what we know from our classic enterprise apps as well, if you want to be really secure, you have to do mutual TLS and also something

called step-up authentication. So, I first authenticate with my service, and if I then want to do a sensitive action, I might be prompted to either log in again, do some MFA with a security key, etc. So, that's the more complex cases when you want to do something sensitive. For example, when you delete some data, transfer some money, then you want to be really sure it's you and

not some rogue LLM moving your money around. >> [snorts] >> So, this sets a bit the background of what we're going to see today. I let AI generate >> a small application for me based on the schedule of JPrime this year. So, we have a small Quarkus application on the one hand that has a simple Postgres database, uses Hibernate, an OpenAPI spec, has some authentication, and then

exposes all the talks of today and tomorrow. Furthermore, we can rate talks, see my schedule, etc. And bookmark talks and even cancel a session. >> That will be the first demo. Then we'll go take it up a notch. So, the first data is just public data, right? JPrime agenda is public. Then personally, it's my agenda and my ratings, so I need to be logged in. And then

in the end, we're going to see some sensitive actions from me as a speaker. For example, if I want to see the raw data of my ratings or if I want to cancel my session, well, we need some extra security to make sure it's not one of you, but it's actually me. So, how does the setup look like? On the one hand, we're going to have our

MCP client. Uh, in this case, we just use the MCP inspector, but we could hook it up to Cloud or whatever uh client you want to use. authentication service. Uh, we're probably running a Keycloak directly on my MacBook here with Docker. then we'll have our Quarkus MCP service. So, that's another service I built uh, that connects to my JPrime schedule. And that's a a simple API again.

So, let's uh see that in action what we So, who here is using Quarkus? A few Not a lot, actually. Uh, so this is the dev UI that shipped with Quarkus. And then we can see the extensions that I have enabled. so we see Hibernate, for example. Uh, OpenID Connect. So, I can see my Keycloak admin panel. And what do we have here? We have our JPrime

realm in there uh, with some clients. So, here you see the different clients registered. And a bunch of demo users. So, this is provided for me by Quarkus dev services that boots up a Keycloak instance. I gave it some configuration. Gave me a Keycloak realm with some So, that's really helpful. Um, if we then have a look at the database, I have some seed data. So, I

have a bunch of people attending And myself. So, I've made up the other names. And in here, we have some other tables. That helps supply the API with all the conference sessions. So, here you probably recognize some of the sessions that are on today. And then we can get some basic data out of the JPrime schedule. If we then have a look at the Swagger UI from

the Open API spec, there we have some basic endpoints some speaker actions So, here we can get some information about sessions. And that that we can use then again in our MCP. Because the current website of JPrime is just static website. I just scrape that data, put it in a database, expose the REST API that we then can Then we have our MCP service. So, with Quarkus

it's quite straightforward to build an actual MCP server. You just add the MCP extension and then Quarkus does the rest of the plumbing for you. And also supports authentication and everything out there. So, if we have a look here, this application is rather straightforward. It doesn't have a lot of extensions. So, it has does have OpenID Connect again for authentication. Has a bunch of REST clients that

connect to my conference REST API. And then it exposes some tools. So, let me make me the bit bigger. what is a tool? In so, an LLM can do tool calling. A couple of years ago not all the models could do tool calling, but the LLM yeah, you can give it a list of tools and the LLM the model will decide when and how to invoke a

tool. So, a tool has a name. So, in this case bookmark a session. And then it has a little description. And this description is more machine readable so that the LLM can when to evoke this tool and also what parameters to give it. And that's basically what an MCP server provides. It just exposes a list of tools that an LLM can discover and then that you can

access. So if we see that in action, let's see. There's a little tool tool called MCP Inspector. And we can connect it. And here we can inspect again the our MCP server that we have built. And we can see the different tools that we have out there. So for example, let's see what we want to do. What's on now? So this is a tool to get the

current sessions. So it should return this Well, in the other hall, there's a talk from Marijn and Piotr about learning modern Java the playful the playful way. And in this hall, we have the practical MCP security in action. Which is this talk. So it works. And here there are a whole bunch of tools that you then can use. For example, I can bookmark a session. For example,

I want to bookmark my own tool session. It does some fuzzy search in the back end. So I can run tool. But now you can already see it in the bottom. It's a bit small. Doesn't render too big. I get an unauthorized exception. So listing sessions, listing speakers, seeing the current schedules, those are public tools. But doing privilege access actions such as bookmarking a session requires authentication.

Let's see where we at. >> So, when I was all the different tools in action that we have set up locally, so we have our conference REST API, we build an MCP server around it, and we can use any MCP client to connect to it. Now, when you actually need to authenticate as well. So, let's see. How do we do that? To register an MCP client, uh

you first need to register an OAuth client in Keycloak. And this is a bit cumbersome, right? if this this is an MCP server running in my own enterprise, I can register it up front, an admin can do it, it can grab the client ID and client secret and configure it. But, you probably don't want to share this client secret with all your colleagues, or if you run

it publicly with the world. So, in this case, they came up with a protocol called uh dynamic client registration, uh where we have uh where the MCP client talks to the MCP server uh and ask for like, "How can I register myself?" So, it has a well-known URL uh for discovery. Then, it detects, "Oh, this is actually pointing me to Keycloak." Then, then your MCP client goes

to Keycloak and then registers itself. Well, then the MCP client is regist- Then, the user can actually log in. Then, we have our normal OAuth 2 flow with PKCE to avoid uh token reuse and token abuse. The user can give consent, so depending on what data you want to access, you can say hey, this MCP server can access my calendar, my Gmail, what not. And then the

AI can do stuff on your behalf. So, let's see that in action. Yeah, this does work. So, the MCP inspector is quite a nice You can inspect all the things and uh try out some things. And also lets you do this MCP client that is set up. So, we can do a quick workflow. That will be the easiest. So, as you can see now get redirected to

my Keycloak realm. That's a typo. And now the client is set up. Well, now you're wondering what it did in the mean in the background. That we can actually check. So, what the my MCP client did, so remember this is the URL of my MCP server, 8081. And this MCP server exposed this well-known endpoint describing where MCP can find the authorization service. So, the resource is this

service. And authorization services, you could have multiple, is my local Keycloak instance. Normally, you wouldn't see localhost here, of course, but this is a demo. So, you see localhost everywhere. So, now my client knows, well, I asked my MCP server, where can I find my authorization service? It got this information back. it's just simple OpenID Connect or OpenIDC specs, also with a well-known URL, and it will

figure out uh, more information about my Keycloak realm. So, then Keycloak returns where can you find uh, how how can you get a token? So, where's my token endpoint for this realm? What stuff does the authorization servers support? Well, this goes on and on and on. Where can I find the public keys that signed my JWTs? but it also contains a place uh, how a client can

register itself. Uh, so then it registers a client. So, it generates a new client for just this instance of my MCP Inspector and registers some callback information and that matches the port that you see on top of us a bit small, but it matches the one that is running here. So, if I would spawn another MCP Inspector on a different port, it will register a new client

for the MC MCP Inspector as well. Well, then you have some metadata about the registered client. now uh, we can do the authorization dance. Uh, so we can get the authorization code and then in the end uh, I can get my access token. So, here we now have a configured access token. So, this MCP Inspector is a client. It didn't know anything. I just pointed it at

my MCP server and the rest is just dynamic discovery uh, described in protocols. Well, then we can now try uh, let's see. Connect, list the tools. Now, I should be able to bookmark a And now you can see because well, I got this token, I'm authorized and authenticated. uh bookmark a talk. Then we can also see this in the back See. >> This should be uh uh

uh uh uh This is the wrong screen. Right? Or No, it was right. And here you can see all the different audit events, what was done on the back end. And in this audit events, um it's not the MCP inspector that did something, but it was actually really me who did something. So, it was me, my I identity got propagated to the back end service all the

way. So, my MCP client sent my access token to the MCP server. The MCP server then accessed uh my REST API that has a schedule with my access token so on my behalf. So, it did something on my behalf. So, it's not a generic uh token, but it's really my access token that got really propagated to the back end. So, this way you can also provide auditing

instead of just a random uh admin API key doing all the actions, you can really see now on the back end like we used to, who did what. Let's go back to the slides. >> So, what we saw in this demo was actually a whole bunch of uh OIDC and all specification. I'm not going to dive too much in there. But, if you're new to that, read

up on on this data online. But, some three newer ones I would say or things to remember in this talk is TCR. So, that's dynamic client registration. So, a client can onboard themselves. And you don't have to manually set up every MCP server. Then we have PKCE. That's proof key for code exchange. And that stops stolen authorization codes from being usable. So, it's just adding another layer

of protection. It's just all spec as well to avoid getting your token stolen. Now, then you go back to the browser. You have your authorization code. And then here the user can really consent. So, the app, my MCP Inspector, never sees any password to the REST API, but we just got to generate an access token all the time. >> Then for people who've been reading the spec

lately, they're going to be, well, DCR is great, but it has some drawbacks and they came up with yet another standard. The problem with DCR is if I open this to the public, or even internally if I have a big company, people are just going to register MCP And you're going to end up with a database with a whole bunch of MCP clients, and you're not sure

if they're even used. Or people might do malicious things, register MCP servers, pretend to be someone else, etc. DCR is nice because it's easy to use, but it has some drawbacks as well. then they came up with CIMD, client ID metadata. And in this case, the client ID is an uh HTTPS URL. So, for example, I'm hosting my service on some URL, and this fully qualified URL

with HTTPS is then going to be my client ID. we can expose some metadata on this endpoint as well, describing who I am. I can do some attestation that I'm really this person, do some software attestation of this. And then with the trust anchors is we have domain ownership. So, we really know if you're running on this domain, well, we can't really forge that. So, that provides

some additional guarantees over the the DCR. With the DCR, you really have to have those clients registered. Uh but with CIMD, it's really stateless again because I don't have to keep track of all those clients. I just specify which host names I would trust. For example, uh and then people can just register the clients as needed. Well, [snorts] we'll be so before Well, mentioned before as well,

uh we can have pre-registered clients where the admin creates a client ID plus secret. And that's, for example, uh if you have some utilities running somewhere, then an admin could provide this key for you. And in the end, we can do a static API key, but I would advise you not to use that. >> So, with all these specs changing and updating all the time, it's really

hard to keep keep updated your keep yourself updated on all those specs. Uh what what do you hear a lot of people saying as well? OAuth was more designed for humans, and with MCP, we need some something called zero trust. So, bearer tokens are not So, that's a frequent objection with the people making the specs. Um but I would argue governance is the question because you really

want to you don't want to per se zero trust. You really want to audit and govern who does what with your MCP service. So, who does what with your data? So, we have an with the who was like the authenticated identity that we can propagate all the way to the back end. With that, we can also have scoped permissions and we can enforce those permissions on the

server, so not on the client side, but we can really enforce those on the servers. and provable, so we can audit, replay, attribute everything. So, I do think OAuth 2 is a good option for MCP servers. So, before we head the next bit of the demo, imagine that we're not only exposing this MCP server for people to use with their coding assistance or LLMs, but we have

a bigger application. That's typically the use case if we build stuff in the enterprise. We're going to have a user that logs into my enterprise application, but most likely is going to use single sign-on with OpenID Connect. And there we then have an identity token and an access token. Then this enterprise application can talk to an MCP server which has some tools to access data in your

database or other REST APIs that you have in your company. And again, my identity hops all the way through. So, all the services know who is making this request, what permissions does this person have? And we can order that and verify it. So, if we go to the next bit, we already saw a bit of the login. but then we're going to now build a full-fledged app,

so we're have going to have a third app. So, that's what you see here, the the enterprise app. That's going to be a new application. And that one is going to interface uh with my MCP server. So, let's have a look there. So, as mentioned, if I try to log into this application, I need to log in So, this time I'm going to log in as an

attendee. This is a bit messed up. >> So, this is my little application, and in this application uh well, I just provided a chat box uh to interact with my MCP server, but I could have more pages where I could export the schedule, see my own data. Uh so, I can ask the questions again, uh what's happening now? So, there's the talk we saw before. here we

can also really see it's a bit hard to read, I must say, which tools were used for this request. So, the LLM picked the tool what's on now, so that's the tool that we expose. And then it renders it nicely, so as to see the talks that we are right now. Then we can also ask it what's up next. And there you can see it picked a

different tool and list the three talks that are coming up next after this >> Uh then I can do actions as an attendee. So, bookmark the JSpecify talk uh later this afternoon from Hinze talking about JSpecify. Uh then I can also list my agenda. So, these are basic actions that I can do as Uh so, I just bookmark this one. So, it's going to show up here.

And if we then have a look again in the audit logs, there we indeed see that the attendee bookmarked this talk that we just saw. So, we can really audit at the backend uh to see what's happening. So, there could have just been a single page application without any AI, without any but now I am uh building an MCP servant on top, an MCP client in my

uh front end application, and then it just behaves as normal. We can have the the LLM doing this, you can really see it was an attendee doing this. Then let's have a look what else can we So, show feedback. Well, I'm attendee. So, I can't show feedback because well, that's a bit weird as an attendee because you don't have a talk. So, let's try that again in

a new session. we can see some feedback, some some fictional feedback on the talks I've given today. And there you can also see uh the different roles that I have in my token, for example. >> So, these are the normal actions I can do, uh but it's really still auditable uh and visible uh who does what. And we can keep track of that. So, this is what

we saw before as well. So, it wasn't the AI that did something, so we can't blame the AI anymore, but we can really tell who did what in the application. So, instead of an AI going rogue, it's really the AI on behalf of me going rogue. So, how do we prevent it from going rogue? We just don't want the AI to cancel all sessions this afternoon, rebuild

the schedule so you're all confused. Uh so, we need something else on top of And that's what they call step-up Uh and this is server-driven, and you can do this mid-conversation. So, for example, uh if you need to sign up for my deep dive this afternoon, and I want to see who's going to be attending this afternoon, uh that's a bit sensitive information, right? We don't want

to expose this to the public. We also probably don't want the AI going running off and exposing all this data. we're going to put a challenge in between that we're not sufficiently authenticated, and that we either need to reauthenticate, uh or be have another challenge with MFA. Well, now that we did the step-up uh we can retry the action, and then it should succeed this time. So,

if we have a little an idea about the sessions, so normal speaker authentication, I can read my session feedback, update my speaker bio, for But what requires step-up authentication is viewing the attendee list, canceling my session, or exporting raw feedback. So, So, are more sensitive actions that you typically have in a web app, uh they ask you to re-prompt again, enter your password before you can do

an export. So, let's see the step-up authentication So, let's make sure that everything So, the tool calling store works. uh how I'm authenticated. I have my default roles and everything, so it should work. I want to see who's signed up for my deep dive this afternoon. Here now the back-end uh throws an error, insufficient user authentication. And this is really the REST API going throwing this exception.

So, that gets propagated through the MCP server and the MCP client. That really says you're not sufficiently authenticated. Uh you need to do MFA. So, I can now hit And of course, it's a demo, so >> [sighs] >> Ah. Let's see. I should have that my vault somewhere. that's interesting. So, what I wanted to show that we now have the step-up authentication with uh auth and that

this ACR is called an authentic uh authentication context request. Uh you typically see this in financial APIs where they are put those uh URL strings uh for financial actions, but we can also use it for for step-up authentication and in this good case I called it silver. Uh so I configured in my key clock realm a step-up uh for the silver ACR uh that when uh an

endpoint has this annotated uh then it re-prompts me to do MFA. So let's see if I can do something else. you know what? Because well, and LLMs are not deterministic, so yeah, sometimes you get this in a demo. I can just try to do something myself. Uh Cancel my Now it says I can't cancel it because for the audit log I need to provide a reason. So

Cancel my deep dive because I [snorts] want to go go home early tonight. Well, and now I was actually able to cancel my deep dive this afternoon. And that's what you now see uh in the audit log as well. I canceled my session, so then it's not the AI canceling my session, but it was really me canceling my session. And you can also see that the token

ACR uh has this uh required silver uh ACR. And for comparison, in the past it just had a one some meaningless number. If we then have a look how that's configured, >> This is a bit hard to find in the UI, so it's in the the realm somewhere, but find it again. it's easier to show you a bit in the code what it does. Yeah, so this

is the filter that we have in there. There you see again this uh you ran with the silver string. Uh if it requests that, uh it's going to redirect me to the step up uh endpoint. And now I'm confused where it is, of course. I think it should be in the >> All right, let's forget about that. I'll I'll share the all the code online. but it's

rather straightforward to add this step on your on your back end. You just add an annotation and put in this value there. And then it requests you the step authentication when you get there. And then let's move on with the slides. So, what we saw with the step-up authentication is not the LM deciding that or your web application, but it's really the server deciding who does the

step-up authentication and decides when more proof is needed. so, this can happen mid-conversation as you saw. Just prompted me with a new window. Do your MFA with Keycloak, and you can configure that in Keycloak as well. I've done that MFA like 5 minutes ago, then it should still be valid and it will automatically redirect you without you prompting again and again to put your MFA code. So,

this policy is entire server-driven. So, the the the server dictates what kind of level you need, and then Keycloak will have the policy or for the re-authentication strategies. Again, this is still the same old OAuth spec, just a bit smarter. So, if you want to have three takeaways for MCP and security, yes, it's possible. That's what we saw today. We can have see who did what. So,

we can have authenticated flow from your front end through all your back ends and have an audit log there. And the identity is propagated through every hop. Uh what the permissions are scoped and enforced at the server. The MCP tool is just a thin door. You define the locks on your actual server. So the MCP tool is just a wrapper around And provable, every action can be

audited and is audited with the token claims that we have. And that's what we're used to doing anyway. And in this case, Quarkus does most of the plumbing with OIDC and OAuth with MCP. Whereas reading all those specs is a lot of work. I tried to keep myself updated on all the specs, but it's really hard to go through them every every time when there's another update.

So Quarkus makes it really easy to do the plumbing around to build an MCP server and implement MCP clients as The only thing is that you have to do is define the policy that who can do what. And you can make it provable at the same time as well. For the people that were taking some pictures, the slides and the code should be all online. So if

you scan the QR code now or take a picture of the QR code, you'll find all the information online. I listed some documentation links as Go read the model the MCP spec as well. It's a long thing, but it does contain interesting things. And of course the Quarkus docs tell you how to build an MCP server yourself. That's it for today. You saw me earlier canceling my

deep dive. That was against a dummy database. So I'll be actually doing my deep dive this afternoon. Hope to see you there. Thank you. >> [applause]

From event

jPrime 2026

03 Jun 2026 – 04 Jun 2026

All event videos
Back to Watch