Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
164115 stories
·
33 followers

BONUS The Scrum Master's AI Prompt Library With Joshua McDonald

1 Share

BONUS: The Scrum Master's AI Prompt Library, with Joshua McDonald

In this BONUS episode, Joshua McDonald shares how he uses AI in the daily work of a Scrum Master, from team summaries and leadership updates to retrospectives and coaching preparation. The conversation is practical and grounded in one clear warning: AI can give Scrum Masters time back, but it cannot replace their judgment.

When AI Stops Being a Toy and Starts Saving Real Time

"It would have to be when it started to save me about one to two hours per day."

 

Joshua's turning point came when AI moved from curiosity to practical assistant. He began using tools like Microsoft Copilot to analyze reports, prepare communication, and package updates for different audiences. The value was not that AI did the thinking for him. The value was that it helped him reach useful coaching conversations faster, especially when working with reports on predictability, volatility, and cycle time.

Turning Flow Data Into Better Coaching Conversations

"It was able to pinpoint where the bottlenecks were, and where I should look."

 

One of Joshua's first high-value AI workflows combined screenshots of team metrics with exported Jira stories. Instead of spending half a day scanning acceptance criteria, transitions, and bottlenecks, he used AI to identify patterns and suggest where to look next. From there, he would ask follow-up questions from the perspective of a Scrum Master, using AI as a sparring partner to prepare coaching talking points before speaking with the team.

AI as a Communication Coach, Not a Replacement for Judgment

"I like to use AI as my coach to help me in my communication style."

 

Joshua is careful with sensitive communication. When he needs to bring data, feedback, or a difficult pattern to an individual or team, he asks AI to help him test the wording. He might ask how a message could land with someone who has reacted defensively before, or request phrases to avoid. In that sense, AI becomes a preparation space: a place to roleplay, refine tone, and make sure the human conversation starts from care rather than accidental bluntness.

A Weekly AI Workflow for Scrum Masters

"I use it pretty much at everything I create."

 

Joshua's weekly rhythm includes AI-generated summaries from standups, backlog refinement, and retrospectives. He uses templates that identify who shared which updates, what action items emerged, and what the team should be able to review later. For retrospectives, he keeps a prompt library on a whiteboard, with prompts adapted for Scrum teams, Kanban teams, and team interests. A hiking team might get a Big Bend retrospective; a car enthusiast might get a BMW-themed one. The point is not novelty for its own sake, but creating retrospectives that feel more connected to the people in the room.

The Prompt Library as a Scrum Master Asset

"I save them. I reuse them. I refine them."

 

Joshua does not treat prompts as throwaway text. He keeps them visible on a whiteboard, labels them by purpose, and refines them over time. That library helps him preserve the structure of useful workflows while adapting the output to the team and context. For Scrum Masters and agile coaches, this is one of the practical takeaways: a prompt library becomes part of the working system, just like a facilitation kit, retrospective catalog, or coaching journal.

Faster Is Not the Same as Better

"I don't lean on it to do critical thinking for me."

 

Joshua connects AI adoption to a deeper risk: using it to produce more work without asking whether that work matters. He validates AI output, checks the data, and stays responsible for conclusions. AI helps him get to an end point faster, but the Scrum Master still owns the thinking, the interpretation, and the impact on people. That distinction matters when teams are being pushed to adopt AI without clear guidance.

What Scrum Masters Should Try on Monday

"Have it coach you. Say, this is my role. This is what's on my agenda for today."

 

Joshua's recommended first experiment is simple: do a personal standup with AI. Share what you did yesterday, what you want to do today, and the challenges in front of you. Then ask AI to act as your coach and help you identify focus, priorities, and possible next steps. Within a week, a Scrum Master can see whether this creates more clarity, better communication, and more intentional use of time.

Learning AI Without Waiting for Permission

"Start there with basic AI fundamentals."

 

For Scrum Masters who are new to AI, Joshua recommends beginning with practical courses on platforms like Udemy, Coursera, or LinkedIn Learning. Then, connect with peers who are already experimenting with prompt creation. The goal is not to become an AI expert overnight. The goal is to understand what AI can support, what it should not replace, and how to build a prompt library that improves your own work.

About Joshua McDonald

Joshua is endlessly curious about better ways of working. He helps teams grow by blending experimentation, creativity, and AI with practical coaching. When a meeting feels routine, he's already testing a new approach to make it more valuable. Energetic and inventive, he turns everyday collaboration into opportunities for team growth.

 

You can link with Joshua McDonald on LinkedIn.





Download audio: https://traffic.libsyn.com/secure/scrummastertoolbox/20261102_Joshua_McDonald_M.mp3?dest-id=246429
Read the whole story
alvinashcraft
53 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

139. Building Software AI Agents Can Actually Work With. Hannes Lowette on Event Sourcing

1 Share

What makes software easier for AI coding agents to understand, maintain, and change? In episode 139 of Betatalks, Rick and Oscar welcome Hannes Lowette to explore event sourcing, domain-driven design, and the architectural choices that help both developers and AI agents work effectively.

Hannes explains how recording meaningful business events—and separating command handlers, projections, and downstream actions—can keep the context of a change small. He shares why this structure could make event-sourced systems a strong fit for AI-assisted software development, while discussing when event sourcing adds value and when it introduces unnecessary complexity.

The conversation covers microservices, modular monoliths, and the pitfalls of choosing architecture based on industry trends rather than business needs and team experience. They also explore AI-assisted legacy code migration, why code quality still matters, and how clear naming, specifications, tests, and Architecture Decision Records (ADRs) support better results.

Beyond the technical discussion, Hannes shares his approach to coaching, knowledge sharing, and conference speaking. Plus, discover how woodworking, a handmade travel guitar with a Visual Studio inlay, and performing with The Linebreakers connect him to the developer community.

An episode for software developers, architects, and technical leads thinking about maintainable systems and the role of AI agents in software engineering.

Follow Betatalks for more conversations about software development, .NET, AI, and software architecture.

About this episode, and Hannes Lowette in particular: you can find Hannes on LinkedIn.

About Betatalks: watch our videos and follow us on Instagram, LinkedIn, and Bluesky. 





Download audio: https://www.buzzsprout.com/1622272/episodes/20008149-139-building-software-ai-agents-can-actually-work-with-hannes-lowette-on-event-sourcing.mp3
Read the whole story
alvinashcraft
53 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Derek Comartin: Architecture in the AI Age - Episode 426

1 Share

https://clearmeasure.com/developers/forums/

Derek Comartin is a senior software developer and Microsoft MVP with 20 years of experience building software across industries including consumer goods, distribution, transportation, manufacturing, and accounting. He is the creator of CodeOpinion, a YouTube channel and blog where he explores software architecture and design topics such as domain-driven design, event sourcing, and decoupling systems; his most recent post, "Decoupling in Software Architecture Moves Complexity," published August 6, 2026, examines how backend processes are affected as systems become more distributed. Derek shares his insights daily on X as @codeopinion and maintains an active GitHub profile with over 100 repositories, including his popular DDD-CQRS-ES-Example project. This will be his third appearance on the podcast, previously joining to discuss migrating to .NET Core and the mindset of a software architect. You can find more about Derek and his work at codeopinion.com.

Code Opinion Website - https://codeopinion.com
LinkedIn - https://www.linkedin.com/in/dcomartin/
Github - https://github.com/dcomartin
X Account - https://x.com/codeopinion

Previous episodes with Derek Comartin:
Episode 104
Episode 188

Want to Learn More?
Visit AzureDevOps.Show for show notes and additional episodes.





Download audio: https://traffic.libsyn.com/clean/secure/azuredevops/Episode_426.mp3?dest-id=768873
Read the whole story
alvinashcraft
53 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

539: Everything from GitHub Universe 2026

1 Share

GitHub Universe’s big theme is bringing AI agents under one roof: James and Frank unpack a shared Copilot harness, sessions that sync across devices and cloud workspaces, and Fusion’s model orchestration. They also weigh practical updates—from connectors and automated code review to projects and agent memory—while sharing where the new tools shine, and where the rough edges remain.

Follow Us

⭐⭐ Review Us ⭐⭐

Machine transcription available on http://mergeconflict.fm

Support Merge Conflict





Download audio: https://aphid.fireside.fm/d/1437767933/02d84890-e58d-43eb-ab4c-26bcc8524289/0f5e420e-1a58-4d30-9a69-3611f5e29609.mp3
Read the whole story
alvinashcraft
53 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Securing an Angular Firebase AI App with Email/Password Authentication (Part 1: Emulator, AuthService, and Route Guard) [GDE]

1 Share

Anyone with the public URL can use my tokens to perform image analysis and text-to-speech, which can quickly drive up my billing costs. To prevent unauthorized usage, I want to convert /dashboard into a protected route by adding Email/Password authentication with Firebase Auth.

Firebase Auth supports many authentication providers out of the box, and I chose Email/Password because it is the simplest method to configure. In Part 1, we will configure the Firebase Auth Local Emulator, build a lazy-loaded AuthService and a functional route guard, and protect the /dashboard route using CanActivateFn.

Architecture & Mental Model

Part 1 focuses on running the Firebase Auth Emulator alongside the Angular dev server and configuring authentication state management.

The architecture uses Firebase's browser session persistence (browserSessionPersistence) to manage the user's authentication state, which provides two key benefits:

  • After a user authenticates successfully, Firebase persists the authentication state in sessionStorage. When the user refreshes the browser page, the browser retains their login state.
  • When the user closes the browser tab, the browser automatically clears the authentication state from sessionStorage. The next time the user visits the application, they must log in again.

Without session persistence, the application would require extra custom logic to check token expiration, explicitly purge the user state from storage, and trigger route redirection.

Similarly, because firebase/auth is a heavy third-party SDK, loading it eagerly would increase the initial bundle size. Instead, AuthService lazy-loads it via a dynamic import() to update the reactive isAuthenticated signal.

flowchart LR
    subgraph Dev["Local Development (npm run launch)"]
        NG["Angular Dev Server (:4200)"]
        EMU["Firebase Auth Emulator (:9099 & UI :4000)"]
    end

    subgraph AuthCore["AuthService (src/app/core/auth)"]
        ENSURE["ensureAuth() (Single-Flight Promise)"]
        SDK["Dynamic import('firebase/auth')"]
        STATE["onAuthStateChanged -> #user Signal"]
        COMP["isAuthenticated Computed Signal"]
        ENSURE --> SDK --> STATE --> COMP
    end

    subgraph Routing["Angular Router (app.routes.ts)"]
        GUARD["canActivateDashboard (CanActivateFn)"]
        HOME["/home (Sign-In Screen)"]
        DASH["/dashboard (Image Analysis & TTS)"]
    end

    NG --> ENSURE
    SDK -. "isLocalhost()" .-> EMU
    GUARD --> ENSURE
    COMP -- "true" --> DASH
    COMP -- "false (Redirect)" --> HOME

1. Configuring the Firebase Auth Emulator & Unified Launch Script

1.1 Enabling the Auth Emulator in firebase.json

The { "emulators": { "auth": ... } } block in firebase/firebase.json configures the Firebase Auth Emulator to run on host port 9099 (127.0.0.1:9099), while "ui": { "enabled": true } enables the Emulator UI on port 4000 (http://127.0.0.1:4000).

// firebase/firebase.json
{
  // ... rest of configuration omitted for brevity
  "emulators": {
    "auth": {
      "port": 9099
    },
    "ui": {
      "enabled": true
    }
  }
}

1.2 Installing concurrently as a Dev Dependency

This step is optional, but I prefer not to start the Firebase Auth Emulator and the Angular dev server in two separate steps—opening one terminal tab to run the emulator and a second terminal tab to run the Angular dev server. Installing concurrently allows us to run both processes with a single command so we can start development immediately.

npm install --save-dev concurrently

1.3 Adding Emulator and Launch Scripts in package.json

During local development, I connect the Angular application to the Firebase Authentication Emulator to seed a test user for login. AuthService connects to the emulator programmatically, and the new scripts in package.json launch it.

// package.json (scripts excerpt)
{
  "scripts": {
    "start": "ng serve",
    "firebase:emulate:auth": "firebase --config firebase/firebase.json emulators:start --only auth",
    "launch": "concurrently \"npm run firebase:emulate:auth\" \"npm run start\""
  }
}

Here is what each script does:

  • start: Starts the local Angular development server (ng serve) on http://localhost:4200.
  • firebase:emulate:auth: Reads the firebase/firebase.json configuration to start the Authentication Emulator only.
  • launch: Launches both the Angular client and the Firebase emulator concurrently, allowing me to prompt the Antigravity CLI to execute it in the background.

2. Creating a Test Account in the Firebase Emulator UI

Seeding a user is a manual process, so I open the Emulator UI at http://localhost:4000 to create a dummy user with the credentials aaa@example.com and 123456. Note that 123456 is a weak password created strictly to facilitate local authentication testing.

Create a new user form in Firebase Emulator Suite
Caption: Creating a new test user with an email and password in the Firebase Emulator UI at http://localhost:4000.

Saving the form creates the user account. Because the users table displays the email address without the password, I recommend using a password that is easy to remember or saving it in a password manager.

User account successfully created in Firebase Emulator Suite
Caption: The newly created email/password user account displayed in the Firebase Authentication Emulator.

3. Building a Lazy-Loaded, Signal-Driven AuthService

3.1 Defining the AuthCredentials Interface

The AuthCredentials interface defines the email and password properties for user login credentials.

// src/app/core/auth/auth-credentials.interface.ts
export interface AuthCredentials {
  email: string;
  password: string;
}

3.2 Implementing AuthService with Dynamic SDK Loading and Signals

First, AuthService injects WINDOW, ConfigService, and DestroyRef, and declares the #user signal, the isAuthenticated computed signal, and private fields to cache the Firebase Auth instance and SDK module:

// src/app/core/auth/auth.service.ts
import { WINDOW } from '@/core/constants/navigator.const';
import { ConfigService, isLocalhost } from '@/core/firebase';
import { computed, DestroyRef, inject, Service, signal } from '@angular/core';
import type { Auth, User } from 'firebase/auth';
import type { AuthCredentials } from './auth-credentials.interface';

const AUTH_EMULATOR_URL = 'http://127.0.0.1:9099';

@Service()
export class AuthService {
  readonly #window = inject(WINDOW);
  readonly #configService = inject(ConfigService);
  readonly #destroyRef$ = inject(DestroyRef);

  readonly #user = signal<User | null>(null);
  user = this.#user.asReadonly();
  isAuthenticated = computed(() => !!this.#user());

  #auth: Auth | null = null;
  #authSdk: typeof import('firebase/auth') | null = null;
  #authReady: Promise<void> | null = null;

Next, AuthService manages the Firebase Auth lifecycle through four core methods:

  1. loadAuth()
    • Dynamically imports firebase/auth for the first time and retrieves the current Firebase application instance.
    • Connects the Angular application to the Firebase Auth Emulator when running on localhost.
    • Sets authentication state persistence to browser session persistence (browserSessionPersistence), registers onAuthStateChanged to monitor authentication state changes, and awaits auth.authStateReady() for the initial auth state to resolve.
    • Stores the auth instance in #auth and the Auth SDK module in #authSdk.
  private async loadAuth(): Promise<void> {
    const [firebaseApp, authSdk] = await Promise.all([this.#configService.getApp(), import('firebase/auth')]);

    const auth = authSdk.getAuth(firebaseApp);
    if (isLocalhost(this.#window)) {
      authSdk.connectAuthEmulator(auth, AUTH_EMULATOR_URL, { disableWarnings: true });
    }

    await authSdk.setPersistence(auth, authSdk.browserSessionPersistence);
    const unsubscribe = authSdk.onAuthStateChanged(auth, (user) => this.#user.set(user));

    this.#destroyRef$.onDestroy(unsubscribe);

    await auth.authStateReady();

    this.#auth = auth;
    this.#authSdk = authSdk;
  }
  1. ensureAuth()
    • Ensures loadAuth() resolves exactly once and returns both the auth instance and the #authSdk module. Keeping both in memory allows signIn() and signOut() to reuse them when logging a user in or out.
  async ensureAuth(): Promise<{ auth: Auth; sdk: typeof import('firebase/auth') }> {
    if (!this.#authReady) {
      this.#authReady = this.loadAuth();
    }

    await this.#authReady;

    if (!this.#auth) {
      throw new Error('Auth is not initialized');
    }

    if (!this.#authSdk) {
      throw new Error('Auth SDK does not exist');
    }

    return { auth: this.#auth, sdk: this.#authSdk };
  }
  1. signIn()
    • Accepts the user's email and password, validates the arguments, and calls sdk.signInWithEmailAndPassword() to authenticate the user. When authentication succeeds, the authSdk.onAuthStateChanged(auth, (user) => this.#user.set(user)) listener observes the state change, updates the #user signal, and recalculates the isAuthenticated computed signal.
  async signIn({ email, password }: AuthCredentials): Promise<void> {
    if (!email || !password) {
      this.#user.set(null);
      return;
    }

    const { auth, sdk } = await this.ensureAuth();
    await sdk.signInWithEmailAndPassword(auth, email, password);
  }
  1. signOut()
    • Calls sdk.signOut(auth) to sign out the currently logged-in user. Similarly, the onAuthStateChanged listener resets the #user signal to null and recalculates the isAuthenticated computed signal.
  async signOut(): Promise<void> {
    const { auth, sdk } = await this.ensureAuth();
    await sdk.signOut(auth);
  }
}

4. Protecting the Dashboard Route with canActivateDashboard

4.1 Creating the CanActivateFn Route Guard

canActivateDashboard is a functional route guard of type CanActivateFn. It injects AuthService and Router to verify the user's authentication status and handle route redirection. Calling await authService.ensureAuth() initializes Firebase Auth (connecting to the Auth Emulator when running on localhost) and updates the isAuthenticated computed signal with the current authentication state. When the user is authenticated, the guard returns true to allow navigation to the route; otherwise, it redirects the user back to /home via router.createUrlTree([APP_LINKS.HOME]).

// src/app/core/auth/auth.guard.ts
import { AuthService } from './auth.service';
import { APP_LINKS } from '@/core/constants/routes.const';
import { inject } from '@angular/core';
import type { CanActivateFn } from '@angular/router';
import { Router } from '@angular/router';

export const canActivateDashboard: CanActivateFn = async () => {
  const authService = inject(AuthService);
  const router = inject(Router);

  await authService.ensureAuth();

  if (authService.isAuthenticated()) {
    return true;
  }

  return router.createUrlTree([APP_LINKS.HOME]);
};

Finally, we re-export the AuthCredentials interface, the canActivateDashboard route guard, and AuthService from a single barrel file (src/app/core/auth/index.ts). This gives the /dashboard route configuration—and the Sign-In component we will build in Part 2—a clean, unified import path.

// src/app/core/auth/index.ts
export * from './auth-credentials.interface';
export * from './auth.guard';
export * from './auth.service';

4.2 Wiring canActivateDashboard in app.routes.ts

The canActivate property of the dashboard route accepts the canActivateDashboard function in an array. When a user types /dashboard in the address bar or navigates to the path programmatically, the router executes this route guard function. Authenticated users proceed to the dashboard, while the guard blocks unauthenticated visitors and redirects them back to /home.

// src/app/core/constants/routes.const.ts
export const ROUTE_PATHS = {
  HOME: 'home',
  DASHBOARD: 'dashboard',
} as const;
// src/app/app.routes.ts (dashboard route excerpt)
  {
    path: ROUTE_PATHS.DASHBOARD,
    title: 'Firebase TTS',
    providers: [provideEnvironmentInitializer(() => inject(ConfigService).initialize())],
    loadComponent: () => import('./features/dashboard/dashboard.component'),
    canActivate: [canActivateDashboard],
  },

Takeaways & What's Next

In Part 1, we configured Firebase Auth to protect our image analysis and TTS endpoints:

  • Local Auth Emulator: Configured the Auth Emulator in firebase.json and used concurrently to start the emulator and Angular app together.
  • Lazy-Loaded AuthService: Dynamically imported firebase/auth once and used signals (#user and isAuthenticated) to track the login state.
  • Route Guard: Added canActivateDashboard to app.routes.ts to redirect unauthenticated users back to the home screen.

In Part 2, we will build the interactive sign-in experience on the frontend. We will use Angular 22's Signal Forms to create the form where users input their email and password, use Angular CDK Dialog to open the sign-in modal when a user clicks the Sign In button on the home screen, and connect the Sign-In component with AuthService to call the signIn() method.

Resources

Read the whole story
alvinashcraft
53 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Use Open.IdentityServer for OpenID Connect in an Aspire setup

1 Share

This article demonstrates how to build an OpenID Connect server using Open.IdentityServer (Rock Solid Knowledge) integrated with ASP.NET Core Identity to provide identity and access management (IAM). The solution is built with Aspire and includes an ASP.NET Core web application and a protected Web API for validating and testing OpenID Connect and OAuth 2.0 standards, flows.

Code: https://github.com/damienbod/OidcOpenIdentityServer

Setup

The solution is built with Aspire and consists of an Open.IdentityServer identity provider, an ASP.NET Core web application, and a protected Web API. Security is implemented using OpenID Connect and OAuth 2.0. The web application uses an OpenID Connect confidential client with the Authorization Code Flow and PKCE, enhanced with OAuth PAR and client assertions for client authentication. The API is secured using delegated access tokens issued by Open.IdentityServer.

Open ID Connect client setup in UI

The following code implements OpenID Connect authentication for an ASP.NET Core application using the Authorization Code Flow. Users authenticate through the identity provider and successful sign-ins are persisted using the __Host-Http-ui-web authentication cookie with SameSite=Lax. Tokens are stored locally for subsequent API access, additional claims are retrieved from the UserInfo endpoint. Custom claims such as loa, loi, and email are explicitly mapped. The setup requests the scope-api scope, uses OAuth Pushed Authorization Requests (PAR) and authenticates the client using a client assertion rather than a client secret.

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    options.DefaultSignOutScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
    options.Cookie.Name = "__Host-Http-ui-web";
    options.Cookie.SameSite = SameSiteMode.Lax;
    // can be strict if same-site
    //options.Cookie.SameSite = SameSiteMode.Strict;
})
.AddOpenIdConnect(options =>
{
    options.Events = OidcEventHandlers.OidcEvents(builder.Configuration);

    options.ClientId = builder.Configuration["WebOidcClientId"];
    options.Authority = builder.Configuration["WebOidcAuthority"];
    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.ResponseType = OpenIdConnectResponseType.Code;

    // client_assertion used, set in oidc events
    //options.ClientSecret = "test";

    options.SaveTokens = true;

    options.GetClaimsFromUserInfoEndpoint = true;
    options.MapInboundClaims = false;

    options.ClaimActions.MapUniqueJsonKey("loa", "loa");
    options.ClaimActions.MapUniqueJsonKey("loi", "loi");
    options.ClaimActions.MapUniqueJsonKey(JwtClaimTypes.Email, JwtClaimTypes.Email);

    // OAuth PAR
    options.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Require;

    options.Scope.Add("scope-api");
    options.TokenValidationParameters = new TokenValidationParameters
    {
        NameClaimType = "name"
    };
});

The OpenID Connect events are implemented in a separate class. The required handlers depend on the flow configuration of the OpenID Connect client. Using OAuth PAR results in different handlers.

using Duende.IdentityModel;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;

namespace Ui;

public static class OidcEventHandlers
{
    public static OpenIdConnectEvents OidcEvents(IConfiguration configuration)
    {
        return new OpenIdConnectEvents
        {
            OnAuthorizationCodeReceived = async context => await OnAuthorizationCodeReceivedHandler(context, configuration),

            // use OAuth PAR
            OnPushAuthorization = async context => await OnPushAuthorizationHandler(context, configuration),

            // standard OIDC flow handlers using JAR and client assertions - not using OAuth PAR
            //OnRedirectToIdentityProvider = async context => await OnRedirectToIdentityProviderHandler(context, configuration),
        };
    }

    private static async Task OnAuthorizationCodeReceivedHandler(AuthorizationCodeReceivedContext context, IConfiguration configuration)
    {
        // https://openid.net/specs/openid-connect-eap-acr-values-1_0-final.html
        if (context.Properties != null && context.Properties.Items.ContainsKey("acr_values"))
        {
            context.ProtocolMessage.AcrValues = context.Properties.Items["acr_values"];
        }

        if (context.TokenEndpointRequest != null)
        {
            context.TokenEndpointRequest.ClientAssertionType = OidcConstants.ClientAssertionTypes.JwtBearer;
            context.TokenEndpointRequest.ClientAssertion = AssertionService.CreateClientToken(configuration);
        }
    }

    /// <summary>
    /// Not using OAuth PAR
    /// </summary>
    //private static async Task OnRedirectToIdentityProviderHandler(RedirectContext context, IConfiguration configuration)
    //{
    //    var request = AssertionService.SignAuthorizationRequest(context.ProtocolMessage, configuration);
    //    var clientId = context.ProtocolMessage.ClientId;
    //    var redirectUri = context.ProtocolMessage.RedirectUri;

    //    context.ProtocolMessage.Parameters.Clear();
    //    context.ProtocolMessage.ClientId = clientId;
    //    context.ProtocolMessage.RedirectUri = redirectUri;
    //    context.ProtocolMessage.SetParameter("request", request);
    //}

    private static async Task OnPushAuthorizationHandler(PushedAuthorizationContext context, IConfiguration configuration)
    {
        context.ProtocolMessage.Parameters.Add("client_assertion", AssertionService.CreateClientToken(configuration));
        context.ProtocolMessage.Parameters.Add("client_assertion_type", OidcConstants.ClientAssertionTypes.JwtBearer);

        context.HandleClientAuthentication();

        // https://openid.net/specs/openid-connect-eap-acr-values-1_0-final.html
        if (context.Properties.Items.ContainsKey("acr_values"))
        {
            context.ProtocolMessage.AcrValues = context.Properties.Items["acr_values"];
        }
    }
}

OpenID Connect features

At present Open.IdentityServer supports a lot of OpenID and OAuth standards. OAuth DPoP and CIBA are two standards not implement at present.

Conclusion

Open.IdentityServer looks like a good solution to implement an identity provider supporting OpenID and OAuth. Rock Solid Knowledge is a mature company. There are in this field a long time and have supported and provided multiple security solutions. I have used for example Admin UI which is well maintained and stable.

Links

https://docs.identityserver.com/openidentityserver/migrating/from_duende

https://github.com/RockSolidKnowledge/Open.IdentityServer

https://docs.identityserver.com/openidentityserver

https://www.identityserver.com

https://aspire.dev



Read the whole story
alvinashcraft
54 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories