[{"data":1,"prerenderedAt":1267},["Reactive",2],{"navigation":3,"/posts/http-clients-oauth2":204,"/posts/http-clients-oauth2-surround":1251},[4,192,200],{"title":5,"_path":6,"children":7,"icon":191},"Blog","/posts",[8,11,14,17,20,23,26,29,32,35,38,41,44,47,50,53,56,59,62,65,68,71,74,77,80,83,86,89,92,95,98,101,104,107,110,113,116,119,122,125,128,131,134,137,140,143,146,149,152,155,158,161,164,167,170,173,176,179,182,185,188],{"title":9,"_path":10},"Testing your API with REST Client","/posts/testing-your-api-with-rest-client",{"title":12,"_path":13},"HTML templating in Xamarin","/posts/html-templating-in-xamarin",{"title":15,"_path":16},"Goodbye Azure Portal, Welcome Azure CLI","/posts/welcome-azure-cli",{"title":18,"_path":19},"Coming across Gitpod","/posts/gitpod",{"title":21,"_path":22},"Handle token retrieval while querying an API","/posts/delegating-handler",{"title":24,"_path":25},"Clean up your local git branches.","/posts/cleaning-git-branches",{"title":27,"_path":28},"Automate configuration of Teams Tab SSO with PowerShell.","/posts/teams-sso-powershell",{"title":30,"_path":31},"How to do a technology watch? - Part 1","/posts/technology-watch-part1",{"title":33,"_path":34},"How to do a technology watch? - Part 2","/posts/technology-watch-part2",{"title":36,"_path":37},"You almost no longer need Key Vault references for Azure Functions.","/posts/azure-functions-custom-configuration",{"title":39,"_path":40},"How to do a technology watch? - Part 3","/posts/technology-watch-part3",{"title":42,"_path":43},"Forget DevOps, the future is already here!","/posts/devops-future",{"title":45,"_path":46},"Week 9, 2021 - Tips I learned this week","/posts/w09-2021-tips-learned-this-week",{"title":48,"_path":49},"Week 12, 2021 - Tips I learned this week","/posts/w12-2021-tips-learned-this-week",{"title":51,"_path":52},"Week 14, 2021 - Tips I learned this week","/posts/w14-2021-tips-learned-this-week",{"title":54,"_path":55},"Once upon a time in .NET","/posts/once-upon-a-time-in-dotnet",{"title":57,"_path":58},"Install your applications with winget","/posts/winget-import",{"title":60,"_path":61},"Customize your applications when installing them with winget","/posts/winget-override",{"title":63,"_path":64},"Week 22, 2021 - Tips I learned this week","/posts/w22-2021-tips-learned-this-week",{"title":66,"_path":67},"How to connect to an Azure SQL Database from C# using Azure AD","/posts/sqlclient-active-directory-authent",{"title":69,"_path":70},"Producing packages for Windows Package Manager","/posts/wingetcreate",{"title":72,"_path":73},"4 tips about GitHub Actions environment variables and contexts","/posts/github-actions-var-and-context",{"title":75,"_path":76},"AzureWebJobsStorage, the secret you don't need in your Function App.","/posts/azure-functions-without-azurewebjobsstorage",{"title":78,"_path":79},"ASP.NET Core - Lost in configuration","/posts/lost-in-configuration",{"title":81,"_path":82},"Week 39, 2021 - Tips I learned this week","/posts/w39-2021-tips-learned-this-week",{"title":84,"_path":85},"Week 41, 2021 - Tips I learned this week","/posts/w41-2021-tips-learned-this-week",{"title":87,"_path":88},"Migrating and open-sourcing my blog","/posts/migrating-blog",{"title":90,"_path":91},"Week 45, 2021 - Tips I learned this week","/posts/w45-2021-tips-learned-this-week",{"title":93,"_path":94},"Organize your GitHub stars with Astral","/posts/astral",{"title":96,"_path":97},"Pulumi with an Azure Blob Storage backend","/posts/pulumi-azure-backend",{"title":99,"_path":100},"IaC Hot Reload with Pulumi Watch","/posts/pulumi-watch",{"title":102,"_path":103},"Week 2, 2022 - Tips I learned this week","/posts/w02-2022-tips-learned-this-week",{"title":105,"_path":106},"Week 3, 2022 - Tips I learned this week","/posts/w03-2022-tips-learned-this-week",{"title":108,"_path":109},"Week 5, 2022 - Tips I learned this week","/posts/w05-2022-tips-learned-this-week",{"title":111,"_path":112},"How to provision an Azure SQL Database with Active Directory authentication","/posts/sqldatabase-active-directory-authent",{"title":114,"_path":115},"Why will I choose Pulumi over Terraform for my next project?","/posts/pulumi-vs-terraform",{"title":117,"_path":118},"Week 19, 2022 - Tips I learned this week","/posts/w19-2022-tips-learned-this-week",{"title":120,"_path":121},"Week 20, 2022 - Tips I learned this week","/posts/w20-2022-tips-learned-this-week",{"title":123,"_path":124},"Keeping secrets secure when using API Clients","/posts/http-clients-secrets",{"title":126,"_path":127},"What made me want to be a developer?","/posts/be-a-developer",{"title":129,"_path":130},"What can we do when stuck with a programming problem?","/posts/get-unstuck",{"title":132,"_path":133},"How did I automate the setup of my developer Windows laptop?","/posts/automate-developer-machine",{"title":135,"_path":136},"Discussion about API clients","/posts/http-clients",{"title":138,"_path":139},"Week 46, 2022 - Tips I learned this week","/posts/w46-2022-tips-learned-this-week",{"title":141,"_path":142},"When Pulumi met Nuke: a .NET love story","/posts/when-pulumi-met-nuke",{"title":144,"_path":145},"A year of learning and sharing - Dev Retro 2022","/posts/2022-retro",{"title":147,"_path":148},"Perform Dynamic Execution of an npm Package","/posts/pnpm-dlx",{"title":150,"_path":151},"Manage multiple Node.js versions","/posts/pnpm-env",{"title":153,"_path":154},"Introducing the Vue.js CI/CD series","/posts/vuecicd-introduction",{"title":156,"_path":157},"Execute commands using your project dependencies","/posts/pnpm-exec",{"title":159,"_path":160},"Vue.js CI/CD: Continuous Integration","/posts/vuecicd-ci",{"title":162,"_path":163},"Who is using pnpm?","/posts/pnpm-who-is-using",{"title":165,"_path":166},"Create an Azure-Ready GitHub Repository using Pulumi","/posts/azure-ready-github-repository",{"title":168,"_path":169},"Deploying to Azure from Azure DevOps without secrets","/posts/ado-workload-identity-federation",{"title":171,"_path":172},"Effortlessly Configure GitHub Repositories for Azure Deployment via OIDC","/posts/scripting-azure-ready-github-repository",{"title":174,"_path":175},"Playing with the .NET 8 Web API template","/posts/playing-with-dotnet8",{"title":177,"_path":178},"Another year of sharing and learning - Dev Retro 2023","/posts/2023-retro",{"title":180,"_path":181},"Week 4, 2024 - Tips I learned this week","/posts/w04-2024-tips-learned-this-week",{"title":183,"_path":184},"Using dependency injection with Azure .NET SDK","/posts/azure-sdk-di",{"title":186,"_path":187},"Having Fun With IT Event Calendars","/posts/it-event-calendars",{"title":189,"_path":190},"Call your Azure AD B2C protected API with authenticated HTTP requests from your JetBrains IDE","/posts/http-clients-oauth2","i-heroicons-newspaper",{"title":193,"_path":194,"children":195,"icon":199},"Goodies","/goodies",[196],{"title":197,"_path":198},"My Git Cheat Sheet","/goodies/gitcheatsheet","i-heroicons-gift-solid",{"title":201,"_path":202,"icon":203},"About","/about","i-heroicons-user-circle-solid",{"_path":190,"_dir":205,"_draft":206,"_partial":206,"_locale":207,"title":189,"description":208,"lead":209,"date":210,"image":211,"badge":213,"tags":215,"ImageAttribution":220,"body":221,"_type":1246,"_id":1247,"_source":1248,"_file":1249,"_extension":1250},"posts",false,"","I have written several blog posts about HTTP clients in the past. I am a big fan of using HTTP text files versioned in a git repository alongside API code and executed by an IDE tooling. However, there was one use case where a GUI tool like Postman or a swagger page was more convenient: retrieving OAuth 2.0 users' tokens. Thanks to the latest OAuth 2.0 feature in JetBrains' IDE built-in HTTP client, this is no longer an issue.","Automatically acquire OAuth 2.0 tokens","2024-03-11T00:00:00.000Z",{"src":212},"/images/access-code-door.webp",{"label":214},"Tooling",[216,217,218,219],"tooling","HTTP","OAuth2","Azure AD B2C","Picture of \u003Ca href=\"https://unsplash.com/fr/@drice22\">Danielle Rice\u003C/a> on \u003Ca href=\"https://unsplash.com/fr/photos/personne-detenant-une-carte-en-noir-et-blanc-7GfRwb78YWs\">Unsplash\u003C/a>",{"type":222,"children":223,"toc":1236},"root",[224,252,259,281,286,291,297,302,331,336,349,368,373,386,392,397,425,432,446,460,465,470,495,500,536,553,566,572,577,582,1083,1091,1099,1112,1118,1123,1128,1140,1153,1194,1199,1205,1210,1230],{"type":225,"tag":226,"props":227,"children":228},"element","p",{},[229,232,241,243,250],{"type":230,"value":231},"text","I have written several ",{"type":225,"tag":233,"props":234,"children":238},"a",{"href":235,"rel":236},"https://www.techwatching.dev/posts/http-clients",[237],"nofollow",[239],{"type":230,"value":240},"blog posts",{"type":230,"value":242}," about HTTP clients in the past. I am a big fan of using HTTP text files versioned in a git repository alongside API code and executed by an IDE tooling. However, there was one use case where a GUI tool like Postman or a swagger page was more convenient: retrieving OAuth 2.0 users' tokens. Thanks to the latest ",{"type":225,"tag":233,"props":244,"children":247},{"href":245,"rel":246},"https://www.jetbrains.com/help/idea/oauth-2-0-authorization.html",[237],[248],{"type":230,"value":249},"OAuth 2.0 feature",{"type":230,"value":251}," in JetBrains' IDE built-in HTTP client, this is no longer an issue.",{"type":225,"tag":253,"props":254,"children":256},"h2",{"id":255},"context",[257],{"type":230,"value":258},"Context",{"type":225,"tag":226,"props":260,"children":261},{},[262,264,270,272,279],{"type":230,"value":263},"I am developing a web application composed of a Vue.js frontend and an ASP.NET Core backend (just describing my use case, technologies don't matter). The end users of this application are authenticated using ",{"type":225,"tag":233,"props":265,"children":268},{"href":266,"rel":267},"https://learn.microsoft.com/en-us/azure/active-directory-b2c/overview",[237],[269],{"type":230,"value":219},{"type":230,"value":271},", which is a ",{"type":225,"tag":233,"props":273,"children":276},{"href":274,"rel":275},"https://en.wikipedia.org/wiki/Customer_identity_access_management",[237],[277],{"type":230,"value":278},"customer identity access management",{"type":230,"value":280}," solution like Auth0 or other competitors.",{"type":225,"tag":226,"props":282,"children":283},{},[284],{"type":230,"value":285},"I often need to manually call the endpoints of the API to verify the code is working properly and that an endpoint is returning the expected result. HTTP files are a convenient way of writing and executing the HTTP requests. Once committed in the Git repository, they can easily be shared with other developers of the team who may not have worked on some endpoints and want to have proper examples with the query parameters and payloads.",{"type":225,"tag":226,"props":287,"children":288},{},[289],{"type":230,"value":290},"As the API is protected by Azure AD B2C, I need to retrieve a valid access token and pass it to my requests.",{"type":225,"tag":253,"props":292,"children":294},{"id":293},"previous-solutions",[295],{"type":230,"value":296},"Previous solutions",{"type":225,"tag":226,"props":298,"children":299},{},[300],{"type":230,"value":301},"Passing a valid access token to my HTTP requests is something I was previously doing by:",{"type":225,"tag":303,"props":304,"children":305},"ul",{},[306,312,317],{"type":225,"tag":307,"props":308,"children":309},"li",{},[310],{"type":230,"value":311},"signing in my frontend",{"type":225,"tag":307,"props":313,"children":314},{},[315],{"type":230,"value":316},"grabbing the token in the web browser dev tools",{"type":225,"tag":307,"props":318,"children":319},{},[320,322,329],{"type":230,"value":321},"copying the token to my ",{"type":225,"tag":233,"props":323,"children":326},{"href":324,"rel":325},"https://www.jetbrains.com/help/idea/exploring-http-syntax.html#environment-variables",[237],[327],{"type":230,"value":328},"HTTP environment variables",{"type":230,"value":330}," (preferably the private environment file to avoid committing a secret in your repository)",{"type":225,"tag":226,"props":332,"children":333},{},[334],{"type":230,"value":335},"That works but:",{"type":225,"tag":303,"props":337,"children":338},{},[339,344],{"type":225,"tag":307,"props":340,"children":341},{},[342],{"type":230,"value":343},"it's cumbersome",{"type":225,"tag":307,"props":345,"children":346},{},[347],{"type":230,"value":348},"you have to do it each time your access token expires",{"type":225,"tag":226,"props":350,"children":351},{},[352,354,366],{"type":230,"value":353},"Another solution is to use a tool that generates app-specific local JWTs and configure your local dev environment to authenticate with these tokens instead of using the Azure AD B2C configuration. In .NET, you can use the ",{"type":225,"tag":233,"props":355,"children":358},{"href":356,"rel":357},"https://learn.microsoft.com/en-us/aspnet/core/security/authentication/jwt-authn",[237],[359],{"type":225,"tag":360,"props":361,"children":363},"code",{"className":362},[],[364],{"type":230,"value":365},"dotnet user-jwts",{"type":230,"value":367}," to do exactly that. It allows you to generate a JWT token with the scopes, roles, and claims you want. So it's a good solution to debug your API locally without having to bypass the authentication and authorization mechanisms.",{"type":225,"tag":226,"props":369,"children":370},{},[371],{"type":230,"value":372},"However, it has some downsides:",{"type":225,"tag":303,"props":374,"children":375},{},[376,381],{"type":225,"tag":307,"props":377,"children":378},{},[379],{"type":230,"value":380},"the tokens are only valid in your local machine so it only works for your local environment",{"type":225,"tag":307,"props":382,"children":383},{},[384],{"type":230,"value":385},"the Azure AD B2C authentication is replaced by this \"local JWT authentication\" so you are not testing your API in real conditions",{"type":225,"tag":253,"props":387,"children":389},{"id":388},"with-the-new-http-client-oauth-20-feature",[390],{"type":230,"value":391},"With the new HTTP Client OAuth 2.0 feature",{"type":225,"tag":226,"props":393,"children":394},{},[395],{"type":230,"value":396},"Starting version 2024.1, HTTP Client in the JetBrains IDEs (in my case Rider 2024.1) support automatically authenticating HTTP requests, provided that you properly configured it.",{"type":225,"tag":398,"props":399,"children":401},"callout",{"icon":400},"i-heroicons-chat-bubble-left-20-solid",[402],{"type":225,"tag":226,"props":403,"children":404},{},[405,407,414,416,423],{"type":230,"value":406},"Support for OAuth 2.0 started in ",{"type":225,"tag":233,"props":408,"children":411},{"href":409,"rel":410},"https://blog.jetbrains.com/idea/2023/10/intellij-idea-2023-3-eap-3/#oauth-2.0-support",[237],[412],{"type":230,"value":413},"version 2023.3",{"type":230,"value":415},", however, Authorization Code Flow with PKCE (PKCE challenge is required in the ",{"type":225,"tag":233,"props":417,"children":420},{"href":418,"rel":419},"https://oauth.net/2.1/",[237],[421],{"type":230,"value":422},"OAuth 2.1 specification",{"type":230,"value":424}," is only supported since 2024.1.",{"type":225,"tag":426,"props":427,"children":429},"h3",{"id":428},"oauth-20-authorization-code-flow-with-pkce",[430],{"type":230,"value":431},"OAuth 2.0 authorization code flow with PKCE",{"type":225,"tag":226,"props":433,"children":434},{},[435,437,444],{"type":230,"value":436},"The OAuth 2.0 flow involved in retrieving a valid access token to make requests to an Azure AD B2C protected API is the authorization code flow with PKCE. There are 2 steps in the ",{"type":225,"tag":233,"props":438,"children":441},{"href":439,"rel":440},"https://learn.microsoft.com/en-us/azure/active-directory-b2c/authorization-code-flow",[237],[442],{"type":230,"value":443},"OAuth 2.0 authorization code flow",{"type":230,"value":445},":",{"type":225,"tag":447,"props":448,"children":449},"ol",{},[450,455],{"type":225,"tag":307,"props":451,"children":452},{},[453],{"type":230,"value":454},"Get an authorization code",{"type":225,"tag":307,"props":456,"children":457},{},[458],{"type":230,"value":459},"Exchange the authorization code for an access token",{"type":225,"tag":226,"props":461,"children":462},{},[463],{"type":230,"value":464},"Step 1 involves the user entering their credentials in the login form (Azure AD B2C login form in this case). At first sight, it might appear not very suitable for using HTTP files but the JetBrains HTTP Client handled it by opening the login form in the IDE embedded browser.",{"type":225,"tag":226,"props":466,"children":467},{},[468],{"type":230,"value":469},"For Azure AD B2C,",{"type":225,"tag":303,"props":471,"children":472},{},[473,484],{"type":225,"tag":307,"props":474,"children":475},{},[476,478],{"type":230,"value":477},"the authorize endpoint is ",{"type":225,"tag":360,"props":479,"children":481},{"className":480},[],[482],{"type":230,"value":483},"https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/authorize",{"type":225,"tag":307,"props":485,"children":486},{},[487,489],{"type":230,"value":488},"the token endpoint is ",{"type":225,"tag":360,"props":490,"children":492},{"className":491},[],[493],{"type":230,"value":494},"https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{policy}/oauth2/v2.0/token",{"type":225,"tag":226,"props":496,"children":497},{},[498],{"type":230,"value":499},"where:",{"type":225,"tag":303,"props":501,"children":502},{},[503,514,525],{"type":225,"tag":307,"props":504,"children":505},{},[506,512],{"type":225,"tag":360,"props":507,"children":509},{"className":508},[],[510],{"type":230,"value":511},"tenant",{"type":230,"value":513}," is the name of the Azure AD B2C tenant",{"type":225,"tag":307,"props":515,"children":516},{},[517,523],{"type":225,"tag":360,"props":518,"children":520},{"className":519},[],[521],{"type":230,"value":522},"clientId",{"type":230,"value":524}," is the application ID of the application registered in Azure AD the B2C tenant",{"type":225,"tag":307,"props":526,"children":527},{},[528,534],{"type":225,"tag":360,"props":529,"children":531},{"className":530},[],[532],{"type":230,"value":533},"policy",{"type":230,"value":535}," is the name of the policy created in the Azure AD B2C tenant",{"type":225,"tag":398,"props":537,"children":539},{"icon":538},"i-heroicons-light-bulb",[540],{"type":225,"tag":226,"props":541,"children":542},{},[543,545,551],{"type":230,"value":544},"When using a custom domain in Azure AD B2C, the endpoints are similar but the ",{"type":225,"tag":360,"props":546,"children":548},{"className":547},[],[549],{"type":230,"value":550},"{tenant}.b2clogin.com",{"type":230,"value":552}," part is replaced by the custom domain.",{"type":225,"tag":226,"props":554,"children":555},{},[556,558,564],{"type":230,"value":557},"If you want to better understand how this flow works, there is a nice diagram in ",{"type":225,"tag":233,"props":559,"children":561},{"href":245,"rel":560},[237],[562],{"type":230,"value":563},"Auth0 documentation",{"type":230,"value":565},".",{"type":225,"tag":426,"props":567,"children":569},{"id":568},"configuration-in-the-jetbrains-http-client",[570],{"type":230,"value":571},"Configuration in the JetBrains HTTP Client",{"type":225,"tag":226,"props":573,"children":574},{},[575],{"type":230,"value":576},"To make the authorization code flow work in the HTTP Client, all I have to do is provide the configuration for the Azure AD B2C tenant in the HTTP environment file.",{"type":225,"tag":226,"props":578,"children":579},{},[580],{"type":230,"value":581},"Here is an example of such configuration:",{"type":225,"tag":583,"props":584,"children":588},"pre",{"className":585,"code":586,"language":587,"meta":207,"style":207},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"apiUrl\": \"https://localhost:5001/api\",\n  \"Security\": {\n    \"Auth\": {\n      \"CIAM\": {\n        \"Type\": \"OAuth2\",\n        \"Grant Type\": \"Authorization Code\",\n        \"PKCE\": true,\n        \"Client ID\": \"3a53c90d-20c4-40e9-b440-4825b70374d7\",\n        \"Scope\": \"openid offline_access profile https://mytenant.onmicrosoft.com/security/user.read\",\n        \"Auth URL\": \"https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/authorize\",\n        \"Token URL\": \"https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/token\",\n        \"Redirect URL\": \"https://localhost:8080/oidc-callback\",\n        \"Acquire Automatically\": true\n      }\n    }\n  }\n}\n","json",[589],{"type":225,"tag":360,"props":590,"children":591},{"__ignoreMap":207},[592,604,648,674,701,728,767,805,831,869,907,945,983,1021,1047,1056,1065,1074],{"type":225,"tag":593,"props":594,"children":597},"span",{"class":595,"line":596},"line",1,[598],{"type":225,"tag":593,"props":599,"children":601},{"style":600},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF",[602],{"type":230,"value":603},"{\n",{"type":225,"tag":593,"props":605,"children":607},{"class":595,"line":606},2,[608,613,619,624,628,633,639,643],{"type":225,"tag":593,"props":609,"children":610},{"style":600},[611],{"type":230,"value":612},"  \"",{"type":225,"tag":593,"props":614,"children":616},{"style":615},"--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA",[617],{"type":230,"value":618},"apiUrl",{"type":225,"tag":593,"props":620,"children":621},{"style":600},[622],{"type":230,"value":623},"\"",{"type":225,"tag":593,"props":625,"children":626},{"style":600},[627],{"type":230,"value":445},{"type":225,"tag":593,"props":629,"children":630},{"style":600},[631],{"type":230,"value":632}," \"",{"type":225,"tag":593,"props":634,"children":636},{"style":635},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[637],{"type":230,"value":638},"https://localhost:5001/api",{"type":225,"tag":593,"props":640,"children":641},{"style":600},[642],{"type":230,"value":623},{"type":225,"tag":593,"props":644,"children":645},{"style":600},[646],{"type":230,"value":647},",\n",{"type":225,"tag":593,"props":649,"children":651},{"class":595,"line":650},3,[652,656,661,665,669],{"type":225,"tag":593,"props":653,"children":654},{"style":600},[655],{"type":230,"value":612},{"type":225,"tag":593,"props":657,"children":658},{"style":615},[659],{"type":230,"value":660},"Security",{"type":225,"tag":593,"props":662,"children":663},{"style":600},[664],{"type":230,"value":623},{"type":225,"tag":593,"props":666,"children":667},{"style":600},[668],{"type":230,"value":445},{"type":225,"tag":593,"props":670,"children":671},{"style":600},[672],{"type":230,"value":673}," {\n",{"type":225,"tag":593,"props":675,"children":677},{"class":595,"line":676},4,[678,683,689,693,697],{"type":225,"tag":593,"props":679,"children":680},{"style":600},[681],{"type":230,"value":682},"    \"",{"type":225,"tag":593,"props":684,"children":686},{"style":685},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[687],{"type":230,"value":688},"Auth",{"type":225,"tag":593,"props":690,"children":691},{"style":600},[692],{"type":230,"value":623},{"type":225,"tag":593,"props":694,"children":695},{"style":600},[696],{"type":230,"value":445},{"type":225,"tag":593,"props":698,"children":699},{"style":600},[700],{"type":230,"value":673},{"type":225,"tag":593,"props":702,"children":704},{"class":595,"line":703},5,[705,710,716,720,724],{"type":225,"tag":593,"props":706,"children":707},{"style":600},[708],{"type":230,"value":709},"      \"",{"type":225,"tag":593,"props":711,"children":713},{"style":712},"--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C",[714],{"type":230,"value":715},"CIAM",{"type":225,"tag":593,"props":717,"children":718},{"style":600},[719],{"type":230,"value":623},{"type":225,"tag":593,"props":721,"children":722},{"style":600},[723],{"type":230,"value":445},{"type":225,"tag":593,"props":725,"children":726},{"style":600},[727],{"type":230,"value":673},{"type":225,"tag":593,"props":729,"children":731},{"class":595,"line":730},6,[732,737,743,747,751,755,759,763],{"type":225,"tag":593,"props":733,"children":734},{"style":600},[735],{"type":230,"value":736},"        \"",{"type":225,"tag":593,"props":738,"children":740},{"style":739},"--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178",[741],{"type":230,"value":742},"Type",{"type":225,"tag":593,"props":744,"children":745},{"style":600},[746],{"type":230,"value":623},{"type":225,"tag":593,"props":748,"children":749},{"style":600},[750],{"type":230,"value":445},{"type":225,"tag":593,"props":752,"children":753},{"style":600},[754],{"type":230,"value":632},{"type":225,"tag":593,"props":756,"children":757},{"style":635},[758],{"type":230,"value":218},{"type":225,"tag":593,"props":760,"children":761},{"style":600},[762],{"type":230,"value":623},{"type":225,"tag":593,"props":764,"children":765},{"style":600},[766],{"type":230,"value":647},{"type":225,"tag":593,"props":768,"children":770},{"class":595,"line":769},7,[771,775,780,784,788,792,797,801],{"type":225,"tag":593,"props":772,"children":773},{"style":600},[774],{"type":230,"value":736},{"type":225,"tag":593,"props":776,"children":777},{"style":739},[778],{"type":230,"value":779},"Grant Type",{"type":225,"tag":593,"props":781,"children":782},{"style":600},[783],{"type":230,"value":623},{"type":225,"tag":593,"props":785,"children":786},{"style":600},[787],{"type":230,"value":445},{"type":225,"tag":593,"props":789,"children":790},{"style":600},[791],{"type":230,"value":632},{"type":225,"tag":593,"props":793,"children":794},{"style":635},[795],{"type":230,"value":796},"Authorization Code",{"type":225,"tag":593,"props":798,"children":799},{"style":600},[800],{"type":230,"value":623},{"type":225,"tag":593,"props":802,"children":803},{"style":600},[804],{"type":230,"value":647},{"type":225,"tag":593,"props":806,"children":808},{"class":595,"line":807},8,[809,813,818,822,826],{"type":225,"tag":593,"props":810,"children":811},{"style":600},[812],{"type":230,"value":736},{"type":225,"tag":593,"props":814,"children":815},{"style":739},[816],{"type":230,"value":817},"PKCE",{"type":225,"tag":593,"props":819,"children":820},{"style":600},[821],{"type":230,"value":623},{"type":225,"tag":593,"props":823,"children":824},{"style":600},[825],{"type":230,"value":445},{"type":225,"tag":593,"props":827,"children":828},{"style":600},[829],{"type":230,"value":830}," true,\n",{"type":225,"tag":593,"props":832,"children":834},{"class":595,"line":833},9,[835,839,844,848,852,856,861,865],{"type":225,"tag":593,"props":836,"children":837},{"style":600},[838],{"type":230,"value":736},{"type":225,"tag":593,"props":840,"children":841},{"style":739},[842],{"type":230,"value":843},"Client ID",{"type":225,"tag":593,"props":845,"children":846},{"style":600},[847],{"type":230,"value":623},{"type":225,"tag":593,"props":849,"children":850},{"style":600},[851],{"type":230,"value":445},{"type":225,"tag":593,"props":853,"children":854},{"style":600},[855],{"type":230,"value":632},{"type":225,"tag":593,"props":857,"children":858},{"style":635},[859],{"type":230,"value":860},"3a53c90d-20c4-40e9-b440-4825b70374d7",{"type":225,"tag":593,"props":862,"children":863},{"style":600},[864],{"type":230,"value":623},{"type":225,"tag":593,"props":866,"children":867},{"style":600},[868],{"type":230,"value":647},{"type":225,"tag":593,"props":870,"children":872},{"class":595,"line":871},10,[873,877,882,886,890,894,899,903],{"type":225,"tag":593,"props":874,"children":875},{"style":600},[876],{"type":230,"value":736},{"type":225,"tag":593,"props":878,"children":879},{"style":739},[880],{"type":230,"value":881},"Scope",{"type":225,"tag":593,"props":883,"children":884},{"style":600},[885],{"type":230,"value":623},{"type":225,"tag":593,"props":887,"children":888},{"style":600},[889],{"type":230,"value":445},{"type":225,"tag":593,"props":891,"children":892},{"style":600},[893],{"type":230,"value":632},{"type":225,"tag":593,"props":895,"children":896},{"style":635},[897],{"type":230,"value":898},"openid offline_access profile https://mytenant.onmicrosoft.com/security/user.read",{"type":225,"tag":593,"props":900,"children":901},{"style":600},[902],{"type":230,"value":623},{"type":225,"tag":593,"props":904,"children":905},{"style":600},[906],{"type":230,"value":647},{"type":225,"tag":593,"props":908,"children":910},{"class":595,"line":909},11,[911,915,920,924,928,932,937,941],{"type":225,"tag":593,"props":912,"children":913},{"style":600},[914],{"type":230,"value":736},{"type":225,"tag":593,"props":916,"children":917},{"style":739},[918],{"type":230,"value":919},"Auth URL",{"type":225,"tag":593,"props":921,"children":922},{"style":600},[923],{"type":230,"value":623},{"type":225,"tag":593,"props":925,"children":926},{"style":600},[927],{"type":230,"value":445},{"type":225,"tag":593,"props":929,"children":930},{"style":600},[931],{"type":230,"value":632},{"type":225,"tag":593,"props":933,"children":934},{"style":635},[935],{"type":230,"value":936},"https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/authorize",{"type":225,"tag":593,"props":938,"children":939},{"style":600},[940],{"type":230,"value":623},{"type":225,"tag":593,"props":942,"children":943},{"style":600},[944],{"type":230,"value":647},{"type":225,"tag":593,"props":946,"children":948},{"class":595,"line":947},12,[949,953,958,962,966,970,975,979],{"type":225,"tag":593,"props":950,"children":951},{"style":600},[952],{"type":230,"value":736},{"type":225,"tag":593,"props":954,"children":955},{"style":739},[956],{"type":230,"value":957},"Token URL",{"type":225,"tag":593,"props":959,"children":960},{"style":600},[961],{"type":230,"value":623},{"type":225,"tag":593,"props":963,"children":964},{"style":600},[965],{"type":230,"value":445},{"type":225,"tag":593,"props":967,"children":968},{"style":600},[969],{"type":230,"value":632},{"type":225,"tag":593,"props":971,"children":972},{"style":635},[973],{"type":230,"value":974},"https://mytenant.b2clogin.com/mytenant.onmicrosoft.com/b2c_1_sign_in/oauth2/v2.0/token",{"type":225,"tag":593,"props":976,"children":977},{"style":600},[978],{"type":230,"value":623},{"type":225,"tag":593,"props":980,"children":981},{"style":600},[982],{"type":230,"value":647},{"type":225,"tag":593,"props":984,"children":986},{"class":595,"line":985},13,[987,991,996,1000,1004,1008,1013,1017],{"type":225,"tag":593,"props":988,"children":989},{"style":600},[990],{"type":230,"value":736},{"type":225,"tag":593,"props":992,"children":993},{"style":739},[994],{"type":230,"value":995},"Redirect URL",{"type":225,"tag":593,"props":997,"children":998},{"style":600},[999],{"type":230,"value":623},{"type":225,"tag":593,"props":1001,"children":1002},{"style":600},[1003],{"type":230,"value":445},{"type":225,"tag":593,"props":1005,"children":1006},{"style":600},[1007],{"type":230,"value":632},{"type":225,"tag":593,"props":1009,"children":1010},{"style":635},[1011],{"type":230,"value":1012},"https://localhost:8080/oidc-callback",{"type":225,"tag":593,"props":1014,"children":1015},{"style":600},[1016],{"type":230,"value":623},{"type":225,"tag":593,"props":1018,"children":1019},{"style":600},[1020],{"type":230,"value":647},{"type":225,"tag":593,"props":1022,"children":1024},{"class":595,"line":1023},14,[1025,1029,1034,1038,1042],{"type":225,"tag":593,"props":1026,"children":1027},{"style":600},[1028],{"type":230,"value":736},{"type":225,"tag":593,"props":1030,"children":1031},{"style":739},[1032],{"type":230,"value":1033},"Acquire Automatically",{"type":225,"tag":593,"props":1035,"children":1036},{"style":600},[1037],{"type":230,"value":623},{"type":225,"tag":593,"props":1039,"children":1040},{"style":600},[1041],{"type":230,"value":445},{"type":225,"tag":593,"props":1043,"children":1044},{"style":600},[1045],{"type":230,"value":1046}," true\n",{"type":225,"tag":593,"props":1048,"children":1050},{"class":595,"line":1049},15,[1051],{"type":225,"tag":593,"props":1052,"children":1053},{"style":600},[1054],{"type":230,"value":1055},"      }\n",{"type":225,"tag":593,"props":1057,"children":1059},{"class":595,"line":1058},16,[1060],{"type":225,"tag":593,"props":1061,"children":1062},{"style":600},[1063],{"type":230,"value":1064},"    }\n",{"type":225,"tag":593,"props":1066,"children":1068},{"class":595,"line":1067},17,[1069],{"type":225,"tag":593,"props":1070,"children":1071},{"style":600},[1072],{"type":230,"value":1073},"  }\n",{"type":225,"tag":593,"props":1075,"children":1077},{"class":595,"line":1076},18,[1078],{"type":225,"tag":593,"props":1079,"children":1080},{"style":600},[1081],{"type":230,"value":1082},"}\n",{"type":225,"tag":398,"props":1084,"children":1085},{"icon":538},[1086],{"type":225,"tag":226,"props":1087,"children":1088},{},[1089],{"type":230,"value":1090},"Instead of setting PKCE to true, you can set if to a JSON object with the code challenge method and code verifier to use in it.",{"type":225,"tag":398,"props":1092,"children":1093},{"icon":400},[1094],{"type":225,"tag":226,"props":1095,"children":1096},{},[1097],{"type":230,"value":1098},"In this example, I have set a local Redirect URL as my front was running locally. But I could also have set the Redirect URL to another environment where my web application is running.",{"type":225,"tag":226,"props":1100,"children":1101},{},[1102,1104,1110],{"type":230,"value":1103},"You can check the ",{"type":225,"tag":233,"props":1105,"children":1107},{"href":245,"rel":1106},[237],[1108],{"type":230,"value":1109},"JetBrains documentation",{"type":230,"value":1111}," to have more information about the HTTP Client support for OAuth 2.0 authorization.",{"type":225,"tag":426,"props":1113,"children":1115},{"id":1114},"authenticated-http-requests-in-the-http-file",[1116],{"type":230,"value":1117},"Authenticated HTTP Requests in the HTTP file",{"type":225,"tag":226,"props":1119,"children":1120},{},[1121],{"type":230,"value":1122},"Once the configuration is set, retrieving an access token can be done with a simple click in the configuration file.",{"type":225,"tag":226,"props":1124,"children":1125},{},[1126],{"type":230,"value":1127},"The authentication process is logged so we can check the requests made and identify any mistakes made in the configuration.",{"type":225,"tag":226,"props":1129,"children":1130},{},[1131],{"type":225,"tag":1132,"props":1133,"children":1139},"img",{"alt":1134,"className":1135,"src":1138},"HTTP authentication log",[1136,1137],"rounded-lg","mx-auto","/posts/images/httpclientsoauht2_1.webp",[],{"type":225,"tag":226,"props":1141,"children":1142},{},[1143,1145,1151],{"type":230,"value":1144},"Hopefully, we don't have to manually retrieve an access token each time we need to execute an HTTP request in an HTTP file of our IDE. We can just use the ",{"type":225,"tag":360,"props":1146,"children":1148},{"className":1147},[],[1149],{"type":230,"value":1150},"{{$auth.token()}}",{"type":230,"value":1152}," variable in the Authorization header of our requests, like this:",{"type":225,"tag":583,"props":1154,"children":1158},{"className":1155,"code":1156,"language":1157,"meta":207,"style":207},"language-http shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","GET {{apiUrl}}/products\nAuthorization: Bearer {{$auth.token(\"CIAM\")}}\n","http",[1159],{"type":225,"tag":360,"props":1160,"children":1161},{"__ignoreMap":207},[1162,1177],{"type":225,"tag":593,"props":1163,"children":1164},{"class":595,"line":596},[1165,1171],{"type":225,"tag":593,"props":1166,"children":1168},{"style":1167},"--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF;--shiki-light-font-style:italic;--shiki-default-font-style:italic;--shiki-dark-font-style:italic",[1169],{"type":230,"value":1170},"GET",{"type":225,"tag":593,"props":1172,"children":1174},{"style":1173},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[1175],{"type":230,"value":1176}," {{apiUrl}}/products\n",{"type":225,"tag":593,"props":1178,"children":1179},{"class":595,"line":606},[1180,1185,1189],{"type":225,"tag":593,"props":1181,"children":1182},{"style":739},[1183],{"type":230,"value":1184},"Authorization",{"type":225,"tag":593,"props":1186,"children":1187},{"style":712},[1188],{"type":230,"value":445},{"type":225,"tag":593,"props":1190,"children":1191},{"style":635},[1192],{"type":230,"value":1193}," Bearer {{$auth.token(\"CIAM\")}}\n",{"type":225,"tag":226,"props":1195,"children":1196},{},[1197],{"type":230,"value":1198},"The IDE will handle the rest for us.",{"type":225,"tag":253,"props":1200,"children":1202},{"id":1201},"wrapping-up",[1203],{"type":230,"value":1204},"Wrapping up",{"type":225,"tag":226,"props":1206,"children":1207},{},[1208],{"type":230,"value":1209},"The HTTP Client OAuth 2.0 feature in JetBrains IDEs has greatly simplified making authenticated HTTP requests to secure APIs. While this article focused on Azure AD B2C, the same principles apply to other Authorization Servers, with only the authorize and token endpoints differing.",{"type":225,"tag":226,"props":1211,"children":1212},{},[1213,1215,1221,1223,1228],{"type":230,"value":1214},"I hope other IDEs will adopt this feature, using the same convention for the ",{"type":225,"tag":360,"props":1216,"children":1218},{"className":1217},[],[1219],{"type":230,"value":1220},"$auth.token()",{"type":230,"value":1222}," variable and its configuration. The only drawback is for developers not using JetBrains IDEs, who will need to adjust requests containing the ",{"type":225,"tag":360,"props":1224,"children":1226},{"className":1225},[],[1227],{"type":230,"value":1220},{"type":230,"value":1229}," variable to run them in their IDEs.",{"type":225,"tag":1231,"props":1232,"children":1233},"style",{},[1234],{"type":230,"value":1235},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":207,"searchDepth":606,"depth":606,"links":1237},[1238,1239,1240,1245],{"id":255,"depth":606,"text":258},{"id":293,"depth":606,"text":296},{"id":388,"depth":606,"text":391,"children":1241},[1242,1243,1244],{"id":428,"depth":650,"text":431},{"id":568,"depth":650,"text":571},{"id":1114,"depth":650,"text":1117},{"id":1201,"depth":606,"text":1204},"markdown","content:1.posts:61.http-clients-oauth2.md","content","1.posts/61.http-clients-oauth2.md","md",[1252,1253],null,{"_path":187,"_dir":205,"_draft":206,"_partial":206,"_locale":207,"title":186,"description":1254,"lead":1255,"date":1256,"image":1257,"badge":1259,"tags":1261,"ImageAttribution":1264,"_type":1246,"_id":1265,"_source":1248,"_file":1266,"_extension":1250},"In this post, we will discuss how to write a small .NET program that retrieves events from an IT event calendar and submits them to another one using AngleSharp.","Playing with AngleSharp","2024-03-04T00:00:00.000Z",{"src":1258},"/images/calendar_1.webp",{"label":1260},"Development",[1262,1263],".NET","AngleSharp","Picture of \u003Ca href=\"https://unsplash.com/fr/@towfiqu999999\">Towfiqu barbhuiya on \u003Ca href=\"https://unsplash.com/fr/photos/un-calendrier-avec-des-boutons-poussoirs-rouges-epingles-bwOAixLG0uc\">Unsplash\u003C/a>","content:1.posts:60.it-event-calendars.md","1.posts/60.it-event-calendars.md",1716749601470]