diff --git a/Libraries/Microsoft.Teams.Api/Activities/Invokes/SignIn/FailureActivity.cs b/Libraries/Microsoft.Teams.Api/Activities/Invokes/SignIn/FailureActivity.cs
new file mode 100644
index 00000000..bec8f919
--- /dev/null
+++ b/Libraries/Microsoft.Teams.Api/Activities/Invokes/SignIn/FailureActivity.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Text.Json.Serialization;
+
+using Microsoft.Teams.Common;
+
+namespace Microsoft.Teams.Api.Activities.Invokes;
+
+public partial class Name : StringEnum
+{
+ public partial class SignIn : StringEnum
+ {
+ public static readonly SignIn Failure = new("signin/failure");
+ public bool IsFailure => Failure.Equals(Value);
+ }
+}
+
+public static partial class SignIn
+{
+ ///
+ /// Represents a signin/failure invoke activity sent by Teams when SSO token exchange fails.
+ ///
+ public class FailureActivity() : SignInActivity(Name.SignIn.Failure)
+ {
+ ///
+ /// A value that is associated with the activity.
+ ///
+ [JsonPropertyName("value")]
+ [JsonPropertyOrder(32)]
+ public new required Api.SignIn.Failure Value
+ {
+ get => (Api.SignIn.Failure)base.Value!;
+ set => base.Value = value;
+ }
+ }
+}
diff --git a/Libraries/Microsoft.Teams.Api/Activities/Invokes/SignInActivity.cs b/Libraries/Microsoft.Teams.Api/Activities/Invokes/SignInActivity.cs
index 6f068fe0..78768308 100644
--- a/Libraries/Microsoft.Teams.Api/Activities/Invokes/SignInActivity.cs
+++ b/Libraries/Microsoft.Teams.Api/Activities/Invokes/SignInActivity.cs
@@ -21,11 +21,13 @@ public abstract class SignInActivity(Name.SignIn name) : InvokeActivity(new(name
{
public SignIn.TokenExchangeActivity ToTokenExchange() => (SignIn.TokenExchangeActivity)this;
public SignIn.VerifyStateActivity ToVerifyState() => (SignIn.VerifyStateActivity)this;
+ public SignIn.FailureActivity ToFailure() => (SignIn.FailureActivity)this;
public override object ToType(Type type, IFormatProvider? provider)
{
if (type == typeof(SignIn.TokenExchangeActivity)) return ToTokenExchange();
if (type == typeof(SignIn.VerifyStateActivity)) return ToVerifyState();
+ if (type == typeof(SignIn.FailureActivity)) return ToFailure();
return this;
}
@@ -56,6 +58,7 @@ public override bool CanConvert(Type typeToConvert)
{
"signin/tokenExchange" => JsonSerializer.Deserialize(element.ToString(), options),
"signin/verifyState" => JsonSerializer.Deserialize(element.ToString(), options),
+ "signin/failure" => JsonSerializer.Deserialize(element.ToString(), options),
_ => throw new JsonException($"failed to deserialize signin activity '{name}' doesn't match any known types.")
};
}
@@ -74,6 +77,12 @@ public override void Write(Utf8JsonWriter writer, SignInActivity value, JsonSeri
return;
}
+ if (value is SignIn.FailureActivity failure)
+ {
+ JsonSerializer.Serialize(writer, failure, options);
+ return;
+ }
+
JsonSerializer.Serialize(writer, value, options);
}
}
diff --git a/Libraries/Microsoft.Teams.Api/SignIn/Failure.cs b/Libraries/Microsoft.Teams.Api/SignIn/Failure.cs
new file mode 100644
index 00000000..320926da
--- /dev/null
+++ b/Libraries/Microsoft.Teams.Api/SignIn/Failure.cs
@@ -0,0 +1,26 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System.Text.Json.Serialization;
+
+namespace Microsoft.Teams.Api.SignIn;
+
+///
+/// Sign-in failure information sent by Teams when SSO token exchange fails.
+///
+public class Failure
+{
+ ///
+ /// The error code for the sign-in failure (e.g., "resourcematchfailed").
+ ///
+ [JsonPropertyName("code")]
+ [JsonPropertyOrder(0)]
+ public string? Code { get; set; }
+
+ ///
+ /// The error message for the sign-in failure.
+ ///
+ [JsonPropertyName("message")]
+ [JsonPropertyOrder(1)]
+ public string? Message { get; set; }
+}
diff --git a/Libraries/Microsoft.Teams.Apps/Activities/Invokes/SignIn/FailureActivity.cs b/Libraries/Microsoft.Teams.Apps/Activities/Invokes/SignIn/FailureActivity.cs
new file mode 100644
index 00000000..5fed8f77
--- /dev/null
+++ b/Libraries/Microsoft.Teams.Apps/Activities/Invokes/SignIn/FailureActivity.cs
@@ -0,0 +1,124 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using Microsoft.Teams.Api.Activities;
+using Microsoft.Teams.Api.Activities.Invokes;
+using Microsoft.Teams.Apps.Routing;
+
+namespace Microsoft.Teams.Apps.Activities.Invokes;
+
+///
+/// Attribute for handling signin/failure invoke activities sent when SSO token exchange fails.
+///
+[AttributeUsage(AttributeTargets.Method, Inherited = true)]
+public class FailureAttribute() : InvokeAttribute(Api.Activities.Invokes.Name.SignIn.Failure, typeof(SignIn.FailureActivity))
+{
+ public override object Coerce(IContext context) => context.ToActivityType();
+}
+
+public static partial class AppInvokeActivityExtensions
+{
+ ///
+ /// Registers a handler for signin/failure invoke activities sent when SSO token exchange fails.
+ ///
+ public static App OnSignInFailure(this App app, Func, Task> handler)
+ {
+ app.Router.Register(new Route()
+ {
+ Name = string.Join("/", [ActivityType.Invoke, Name.SignIn.Failure]),
+ Type = app.Status is null ? RouteType.System : RouteType.User,
+ Handler = async context =>
+ {
+ await handler(context.ToActivityType());
+ return null;
+ },
+ Selector = activity => activity is SignIn.FailureActivity
+ });
+
+ return app;
+ }
+
+ ///
+ /// Registers a handler for signin/failure invoke activities sent when SSO token exchange fails.
+ ///
+ public static App OnSignInFailure(this App app, Func, Task