diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Amazon.Lambda.Annotations.SourceGenerator.csproj b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Amazon.Lambda.Annotations.SourceGenerator.csproj
index 3c894f59b..c435c7c8a 100644
--- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Amazon.Lambda.Annotations.SourceGenerator.csproj
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Amazon.Lambda.Annotations.SourceGenerator.csproj
@@ -103,6 +103,14 @@
TextTemplatingFilePreprocessor
ExecutableAssembly.cs
+
+ TextTemplatingFilePreprocessor
+ AuthorizerSetupParameters.cs
+
+
+ TextTemplatingFilePreprocessor
+ AuthorizerInvoke.cs
+
@@ -136,6 +144,16 @@
True
ExecutableAssembly.tt
+
+ True
+ True
+ AuthorizerSetupParameters.tt
+
+
+ True
+ True
+ AuthorizerInvoke.tt
+
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/AttributeModelBuilder.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/AttributeModelBuilder.cs
index 1a62078f3..328a29ac5 100644
--- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/AttributeModelBuilder.cs
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/Attributes/AttributeModelBuilder.cs
@@ -90,6 +90,24 @@ public static AttributeModel Build(AttributeData att, GeneratorExecutionContext
Type = TypeModelBuilder.Build(att.AttributeClass, context)
};
}
+ else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.HttpApiAuthorizerAttribute), SymbolEqualityComparer.Default))
+ {
+ var data = HttpApiAuthorizerAttributeBuilder.Build(att);
+ model = new AttributeModel
+ {
+ Data = data,
+ Type = TypeModelBuilder.Build(att.AttributeClass, context)
+ };
+ }
+ else if (att.AttributeClass.Equals(context.Compilation.GetTypeByMetadataName(TypeFullNames.RestApiAuthorizerAttribute), SymbolEqualityComparer.Default))
+ {
+ var data = RestApiAuthorizerAttributeBuilder.Build(att);
+ model = new AttributeModel
+ {
+ Data = data,
+ Type = TypeModelBuilder.Build(att.AttributeClass, context)
+ };
+ }
else
{
model = new AttributeModel
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventType.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventType.cs
index 512319a79..d231967e3 100644
--- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventType.cs
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventType.cs
@@ -10,6 +10,7 @@ public enum EventType
S3,
SQS,
DynamoDB,
- Schedule
+ Schedule,
+ Authorizer
}
}
\ No newline at end of file
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventTypeBuilder.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventTypeBuilder.cs
index f70260803..3f5775851 100644
--- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventTypeBuilder.cs
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/EventTypeBuilder.cs
@@ -26,6 +26,11 @@ public static HashSet Build(IMethodSymbol lambdaMethodSymbol,
{
events.Add(EventType.SQS);
}
+ else if (attribute.AttributeClass.ToDisplayString() == TypeFullNames.HttpApiAuthorizerAttribute
+ || attribute.AttributeClass.ToDisplayString() == TypeFullNames.RestApiAuthorizerAttribute)
+ {
+ events.Add(EventType.Authorizer);
+ }
}
return events;
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/GeneratedMethodModelBuilder.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/GeneratedMethodModelBuilder.cs
index f39a1d440..decb864ee 100644
--- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/GeneratedMethodModelBuilder.cs
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/GeneratedMethodModelBuilder.cs
@@ -55,7 +55,7 @@ private static IList BuildUsings(LambdaMethodModel lambdaMethodModel,
namespaces.Add("Amazon.Lambda.Core");
- if(lambdaMethodModel.ReturnsIHttpResults)
+ if(lambdaMethodModel.ReturnsIHttpResults || lambdaMethodModel.ReturnsIAuthorizerResult)
{
namespaces.Add("Amazon.Lambda.Annotations.APIGateway");
}
@@ -79,6 +79,26 @@ private static TypeModel BuildResponseType(IMethodSymbol lambdaMethodSymbol,
return TypeModelBuilder.Build(typeStream, context);
}
+ // For authorizer functions returning IAuthorizerResult, the generated handler returns Stream
+ // (the IAuthorizerResult.Serialize method produces the JSON stream)
+ if (lambdaMethodModel.ReturnsIAuthorizerResult)
+ {
+ var typeStream = context.Compilation.GetTypeByMetadataName(TypeFullNames.Stream);
+ if (lambdaMethodModel.ReturnsGenericTask)
+ {
+ var genericTask = task.Construct(typeStream);
+ return TypeModelBuilder.Build(genericTask, context);
+ }
+ return TypeModelBuilder.Build(typeStream, context);
+ }
+
+ // For authorizer functions that return raw API Gateway types (backwards compatibility),
+ // pass through the return type as-is
+ if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.HttpApiAuthorizerAttribute) ||
+ lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAuthorizerAttribute))
+ {
+ return lambdaMethodModel.ReturnType;
+ }
if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAttribute))
{
@@ -143,6 +163,20 @@ private static HttpApiVersion GetHttpApiVersion(IMethodSymbol lambdaMethodSymbol
return (HttpApiVersion)versionArgument.Value;
}
+ private static AuthorizerPayloadFormatVersion GetAuthorizerPayloadFormatVersion(AttributeData authorizerAttribute)
+ {
+ var versionArg = authorizerAttribute.NamedArguments
+ .FirstOrDefault(arg => arg.Key == "AuthorizerPayloadFormatVersion").Value;
+
+ if (versionArg.Type == null || versionArg.Value == null)
+ {
+ // Default is V2
+ return AuthorizerPayloadFormatVersion.V2;
+ }
+
+ return (AuthorizerPayloadFormatVersion)(int)versionArg.Value;
+ }
+
private static IList BuildParameters(IMethodSymbol lambdaMethodSymbol,
LambdaMethodModel lambdaMethodModel, GeneratorExecutionContext context)
{
@@ -158,7 +192,48 @@ private static IList BuildParameters(IMethodSymbol lambdaMethodS
Documentation = "The ILambdaContext that provides methods for logging and describing the Lambda environment."
};
- if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAttribute))
+ if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.HttpApiAuthorizerAttribute))
+ {
+ // For HTTP API authorizer functions, the generated handler accepts the authorizer request type
+ var authorizerAttribute = lambdaMethodSymbol.GetAttributeData(context, TypeFullNames.HttpApiAuthorizerAttribute);
+ var payloadVersion = GetAuthorizerPayloadFormatVersion(authorizerAttribute);
+
+ string requestTypeName;
+ if (payloadVersion == AuthorizerPayloadFormatVersion.V2)
+ {
+ requestTypeName = TypeFullNames.APIGatewayCustomAuthorizerV2Request;
+ }
+ else
+ {
+ requestTypeName = TypeFullNames.APIGatewayCustomAuthorizerRequest;
+ }
+
+ var symbol = context.Compilation.GetTypeByMetadataName(requestTypeName);
+ var type = TypeModelBuilder.Build(symbol, context);
+ var requestParameter = new ParameterModel
+ {
+ Name = "__request__",
+ Type = type,
+ Documentation = "The API Gateway authorizer request object that will be processed by the Lambda function handler."
+ };
+ parameters.Add(requestParameter);
+ parameters.Add(contextParameter);
+ }
+ else if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAuthorizerAttribute))
+ {
+ // For REST API authorizer functions, always use APIGatewayCustomAuthorizerRequest
+ var symbol = context.Compilation.GetTypeByMetadataName(TypeFullNames.APIGatewayCustomAuthorizerRequest);
+ var type = TypeModelBuilder.Build(symbol, context);
+ var requestParameter = new ParameterModel
+ {
+ Name = "__request__",
+ Type = type,
+ Documentation = "The API Gateway authorizer request object that will be processed by the Lambda function handler."
+ };
+ parameters.Add(requestParameter);
+ parameters.Add(contextParameter);
+ }
+ else if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAttribute))
{
var symbol = context.Compilation.GetTypeByMetadataName(TypeFullNames.APIGatewayProxyRequest);
var type = TypeModelBuilder.Build(symbol, context);
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/LambdaMethodModel.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/LambdaMethodModel.cs
index 06d032eff..df80c43e5 100644
--- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/LambdaMethodModel.cs
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Models/LambdaMethodModel.cs
@@ -64,6 +64,31 @@ public bool ReturnsIHttpResults
}
}
+ ///
+ /// Returns true if the Lambda function returns either IAuthorizerResult or Task<IAuthorizerResult>
+ ///
+ public bool ReturnsIAuthorizerResult
+ {
+ get
+ {
+ if (ReturnsVoid)
+ {
+ return false;
+ }
+
+ if (ReturnType.FullName == TypeFullNames.IAuthorizerResult)
+ {
+ return true;
+ }
+ if (ReturnsGenericTask && ReturnType.TypeArguments.Count == 1 && ReturnType.TypeArguments[0].FullName == TypeFullNames.IAuthorizerResult)
+ {
+ return true;
+ }
+
+ return false;
+ }
+ }
+
///
/// Returns true if the Lambda function returns either void, Task, SQSBatchResponse or Task
///
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerInvoke.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerInvoke.cs
new file mode 100644
index 000000000..9ca10800b
--- /dev/null
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerInvoke.cs
@@ -0,0 +1,433 @@
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version: 18.0.0.0
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+namespace Amazon.Lambda.Annotations.SourceGenerator.Templates
+{
+ using System.Linq;
+ using System.Text;
+ using System.Collections.Generic;
+ using Amazon.Lambda.Annotations.SourceGenerator.Extensions;
+ using Amazon.Lambda.Annotations.SourceGenerator.Models;
+ using Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes;
+ using System;
+
+ ///
+ /// Class to produce the template output
+ ///
+
+ #line 1 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
+ public partial class AuthorizerInvoke : AuthorizerInvokeBase
+ {
+#line hidden
+ ///
+ /// Create the template output
+ ///
+ public virtual string TransformText()
+ {
+
+ #line 9 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+
+ var httpApiAuthorizerAttribute = _model.LambdaMethod.Attributes.FirstOrDefault(att => att.Type.FullName == TypeFullNames.HttpApiAuthorizerAttribute) as AttributeModel;
+ var restApiAuthorizerAttribute = _model.LambdaMethod.Attributes.FirstOrDefault(att => att.Type.FullName == TypeFullNames.RestApiAuthorizerAttribute) as AttributeModel;
+
+ if (_model.LambdaMethod.ReturnsIAuthorizerResult)
+ {
+ // Determine the serialization format based on the authorizer attribute
+ string format;
+ if (httpApiAuthorizerAttribute != null && httpApiAuthorizerAttribute.Data.EnableSimpleResponses)
+ {
+ format = "AuthorizerResultSerializationOptions.AuthorizerFormat.HttpApiSimple";
+ }
+ else if (httpApiAuthorizerAttribute != null)
+ {
+ format = "AuthorizerResultSerializationOptions.AuthorizerFormat.HttpApiIamPolicy";
+ }
+ else
+ {
+ format = "AuthorizerResultSerializationOptions.AuthorizerFormat.RestApi";
+ }
+
+ // Determine if MethodArn is available (V1/REST have it, V2 has RouteArn)
+ var isV2 = httpApiAuthorizerAttribute != null && httpApiAuthorizerAttribute.Data.AuthorizerPayloadFormatVersion == Amazon.Lambda.Annotations.APIGateway.AuthorizerPayloadFormatVersion.V2;
+ var methodArnExpression = isV2 ? "__request__.RouteArn" : "__request__.MethodArn";
+
+
+ #line default
+ #line hidden
+ this.Write(" var authorizerResult = ");
+
+ #line 34 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(_model.LambdaMethod.ReturnsGenericTask ? "await " : ""));
+
+ #line default
+ #line hidden
+
+ #line 34 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(_model.LambdaMethod.ContainingType.Name.ToCamelCase()));
+
+ #line default
+ #line hidden
+ this.Write(".");
+
+ #line 34 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(_model.LambdaMethod.Name));
+
+ #line default
+ #line hidden
+ this.Write("(");
+
+ #line 34 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(_parameterSignature));
+
+ #line default
+ #line hidden
+ this.Write(");\r\n var serializationOptions = new AuthorizerResultSerializationOptio" +
+ "ns\r\n {\r\n Format = ");
+
+ #line 37 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(format));
+
+ #line default
+ #line hidden
+ this.Write(",\r\n MethodArn = ");
+
+ #line 38 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(methodArnExpression));
+
+ #line default
+ #line hidden
+ this.Write("\r\n };\r\n var response = (System.IO.Stream)authorizerResult.S" +
+ "erialize(serializationOptions);\r\n return response;\r\n");
+
+ #line 42 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+
+ }
+ else
+ {
+ // Raw return type - pass through directly (backwards compatibility)
+
+
+ #line default
+ #line hidden
+ this.Write(" var response = ");
+
+ #line 48 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(_model.LambdaMethod.ReturnsGenericTask ? "await " : ""));
+
+ #line default
+ #line hidden
+
+ #line 48 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(_model.LambdaMethod.ContainingType.Name.ToCamelCase()));
+
+ #line default
+ #line hidden
+ this.Write(".");
+
+ #line 48 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(_model.LambdaMethod.Name));
+
+ #line default
+ #line hidden
+ this.Write("(");
+
+ #line 48 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(_parameterSignature));
+
+ #line default
+ #line hidden
+ this.Write(");\r\n return response;\r\n");
+
+ #line 50 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerInvoke.tt"
+
+ }
+
+
+ #line default
+ #line hidden
+ return this.GenerationEnvironment.ToString();
+ }
+ }
+
+ #line default
+ #line hidden
+ #region Base class
+ ///
+ /// Base class for this transformation
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
+ public class AuthorizerInvokeBase
+ {
+ #region Fields
+ private global::System.Text.StringBuilder generationEnvironmentField;
+ private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;
+ private global::System.Collections.Generic.List indentLengthsField;
+ private string currentIndentField = "";
+ private bool endsWithNewline;
+ private global::System.Collections.Generic.IDictionary sessionField;
+ #endregion
+ #region Properties
+ ///
+ /// The string builder that generation-time code is using to assemble generated output
+ ///
+ public System.Text.StringBuilder GenerationEnvironment
+ {
+ get
+ {
+ if ((this.generationEnvironmentField == null))
+ {
+ this.generationEnvironmentField = new global::System.Text.StringBuilder();
+ }
+ return this.generationEnvironmentField;
+ }
+ set
+ {
+ this.generationEnvironmentField = value;
+ }
+ }
+ ///
+ /// The error collection for the generation process
+ ///
+ public System.CodeDom.Compiler.CompilerErrorCollection Errors
+ {
+ get
+ {
+ if ((this.errorsField == null))
+ {
+ this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();
+ }
+ return this.errorsField;
+ }
+ }
+ ///
+ /// A list of the lengths of each indent that was added with PushIndent
+ ///
+ private System.Collections.Generic.List indentLengths
+ {
+ get
+ {
+ if ((this.indentLengthsField == null))
+ {
+ this.indentLengthsField = new global::System.Collections.Generic.List();
+ }
+ return this.indentLengthsField;
+ }
+ }
+ ///
+ /// Gets the current indent we use when adding lines to the output
+ ///
+ public string CurrentIndent
+ {
+ get
+ {
+ return this.currentIndentField;
+ }
+ }
+ ///
+ /// Current transformation session
+ ///
+ public virtual global::System.Collections.Generic.IDictionary Session
+ {
+ get
+ {
+ return this.sessionField;
+ }
+ set
+ {
+ this.sessionField = value;
+ }
+ }
+ #endregion
+ #region Transform-time helpers
+ ///
+ /// Write text directly into the generated output
+ ///
+ public void Write(string textToAppend)
+ {
+ if (string.IsNullOrEmpty(textToAppend))
+ {
+ return;
+ }
+ // If we're starting off, or if the previous text ended with a newline,
+ // we have to append the current indent first.
+ if (((this.GenerationEnvironment.Length == 0)
+ || this.endsWithNewline))
+ {
+ this.GenerationEnvironment.Append(this.currentIndentField);
+ this.endsWithNewline = false;
+ }
+ // Check if the current text ends with a newline
+ if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
+ {
+ this.endsWithNewline = true;
+ }
+ // This is an optimization. If the current indent is "", then we don't have to do any
+ // of the more complex stuff further down.
+ if ((this.currentIndentField.Length == 0))
+ {
+ this.GenerationEnvironment.Append(textToAppend);
+ return;
+ }
+ // Everywhere there is a newline in the text, add an indent after it
+ textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
+ // If the text ends with a newline, then we should strip off the indent added at the very end
+ // because the appropriate indent will be added when the next time Write() is called
+ if (this.endsWithNewline)
+ {
+ this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
+ }
+ else
+ {
+ this.GenerationEnvironment.Append(textToAppend);
+ }
+ }
+ ///
+ /// Write text directly into the generated output
+ ///
+ public void WriteLine(string textToAppend)
+ {
+ this.Write(textToAppend);
+ this.GenerationEnvironment.AppendLine();
+ this.endsWithNewline = true;
+ }
+ ///
+ /// Write formatted text directly into the generated output
+ ///
+ public void Write(string format, params object[] args)
+ {
+ this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
+ }
+ ///
+ /// Write formatted text directly into the generated output
+ ///
+ public void WriteLine(string format, params object[] args)
+ {
+ this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
+ }
+ ///
+ /// Raise an error
+ ///
+ public void Error(string message)
+ {
+ System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
+ error.ErrorText = message;
+ this.Errors.Add(error);
+ }
+ ///
+ /// Raise a warning
+ ///
+ public void Warning(string message)
+ {
+ System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
+ error.ErrorText = message;
+ error.IsWarning = true;
+ this.Errors.Add(error);
+ }
+ ///
+ /// Increase the indent
+ ///
+ public void PushIndent(string indent)
+ {
+ if ((indent == null))
+ {
+ throw new global::System.ArgumentNullException("indent");
+ }
+ this.currentIndentField = (this.currentIndentField + indent);
+ this.indentLengths.Add(indent.Length);
+ }
+ ///
+ /// Remove the last indent that was added with PushIndent
+ ///
+ public string PopIndent()
+ {
+ string returnValue = "";
+ if ((this.indentLengths.Count > 0))
+ {
+ int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
+ this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
+ if ((indentLength > 0))
+ {
+ returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
+ this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
+ }
+ }
+ return returnValue;
+ }
+ ///
+ /// Remove any indentation
+ ///
+ public void ClearIndent()
+ {
+ this.indentLengths.Clear();
+ this.currentIndentField = "";
+ }
+ #endregion
+ #region ToString Helpers
+ ///
+ /// Utility class to produce culture-oriented representation of an object as a string.
+ ///
+ public class ToStringInstanceHelper
+ {
+ private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture;
+ ///
+ /// Gets or sets format provider to be used by ToStringWithCulture method.
+ ///
+ public System.IFormatProvider FormatProvider
+ {
+ get
+ {
+ return this.formatProviderField ;
+ }
+ set
+ {
+ if ((value != null))
+ {
+ this.formatProviderField = value;
+ }
+ }
+ }
+ ///
+ /// This is called from the compile/run appdomain to convert objects within an expression block to a string
+ ///
+ public string ToStringWithCulture(object objectToConvert)
+ {
+ if ((objectToConvert == null))
+ {
+ throw new global::System.ArgumentNullException("objectToConvert");
+ }
+ System.Type t = objectToConvert.GetType();
+ System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] {
+ typeof(System.IFormatProvider)});
+ if ((method == null))
+ {
+ return objectToConvert.ToString();
+ }
+ else
+ {
+ return ((string)(method.Invoke(objectToConvert, new object[] {
+ this.formatProviderField })));
+ }
+ }
+ }
+ private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
+ ///
+ /// Helper to produce culture-oriented representation of an object as a string
+ ///
+ public ToStringInstanceHelper ToStringHelper
+ {
+ get
+ {
+ return this.toStringHelperField;
+ }
+ }
+ #endregion
+ }
+ #endregion
+}
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerInvoke.tt b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerInvoke.tt
new file mode 100644
index 000000000..fda02bc7a
--- /dev/null
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerInvoke.tt
@@ -0,0 +1,52 @@
+<#@ template language="C#" #>
+<#@ assembly name="System.Core" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Text" #>
+<#@ import namespace="System.Collections.Generic" #>
+<#@ import namespace="Amazon.Lambda.Annotations.SourceGenerator.Extensions" #>
+<#@ import namespace="Amazon.Lambda.Annotations.SourceGenerator.Models" #>
+<#@ import namespace="Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes" #>
+<#
+ var httpApiAuthorizerAttribute = _model.LambdaMethod.Attributes.FirstOrDefault(att => att.Type.FullName == TypeFullNames.HttpApiAuthorizerAttribute) as AttributeModel;
+ var restApiAuthorizerAttribute = _model.LambdaMethod.Attributes.FirstOrDefault(att => att.Type.FullName == TypeFullNames.RestApiAuthorizerAttribute) as AttributeModel;
+
+ if (_model.LambdaMethod.ReturnsIAuthorizerResult)
+ {
+ // Determine the serialization format based on the authorizer attribute
+ string format;
+ if (httpApiAuthorizerAttribute != null && httpApiAuthorizerAttribute.Data.EnableSimpleResponses)
+ {
+ format = "AuthorizerResultSerializationOptions.AuthorizerFormat.HttpApiSimple";
+ }
+ else if (httpApiAuthorizerAttribute != null)
+ {
+ format = "AuthorizerResultSerializationOptions.AuthorizerFormat.HttpApiIamPolicy";
+ }
+ else
+ {
+ format = "AuthorizerResultSerializationOptions.AuthorizerFormat.RestApi";
+ }
+
+ // Determine if MethodArn is available (V1/REST have it, V2 has RouteArn)
+ var isV2 = httpApiAuthorizerAttribute != null && httpApiAuthorizerAttribute.Data.AuthorizerPayloadFormatVersion == Amazon.Lambda.Annotations.APIGateway.AuthorizerPayloadFormatVersion.V2;
+ var methodArnExpression = isV2 ? "__request__.RouteArn" : "__request__.MethodArn";
+#>
+ var authorizerResult = <#= _model.LambdaMethod.ReturnsGenericTask ? "await " : "" #><#= _model.LambdaMethod.ContainingType.Name.ToCamelCase() #>.<#= _model.LambdaMethod.Name #>(<#= _parameterSignature #>);
+ var serializationOptions = new AuthorizerResultSerializationOptions
+ {
+ Format = <#= format #>,
+ MethodArn = <#= methodArnExpression #>
+ };
+ var response = (System.IO.Stream)authorizerResult.Serialize(serializationOptions);
+ return response;
+<#
+ }
+ else
+ {
+ // Raw return type - pass through directly (backwards compatibility)
+#>
+ var response = <#= _model.LambdaMethod.ReturnsGenericTask ? "await " : "" #><#= _model.LambdaMethod.ContainingType.Name.ToCamelCase() #>.<#= _model.LambdaMethod.Name #>(<#= _parameterSignature #>);
+ return response;
+<#
+ }
+#>
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerInvokeCode.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerInvokeCode.cs
new file mode 100644
index 000000000..3fceeea78
--- /dev/null
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerInvokeCode.cs
@@ -0,0 +1,17 @@
+using Amazon.Lambda.Annotations.SourceGenerator.Models;
+
+namespace Amazon.Lambda.Annotations.SourceGenerator.Templates
+{
+ public partial class AuthorizerInvoke
+ {
+ private readonly LambdaFunctionModel _model;
+
+ public readonly string _parameterSignature;
+
+ public AuthorizerInvoke(LambdaFunctionModel model, string parameterSignature)
+ {
+ _model = model;
+ _parameterSignature = parameterSignature;
+ }
+ }
+}
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerSetupParameters.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerSetupParameters.cs
new file mode 100644
index 000000000..bb17ef2d8
--- /dev/null
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerSetupParameters.cs
@@ -0,0 +1,684 @@
+// ------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version: 18.0.0.0
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+namespace Amazon.Lambda.Annotations.SourceGenerator.Templates
+{
+ using System.Linq;
+ using System.Text;
+ using System.Collections.Generic;
+ using Amazon.Lambda.Annotations.SourceGenerator.Extensions;
+ using Amazon.Lambda.Annotations.SourceGenerator.Models;
+ using Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes;
+ using System;
+
+ ///
+ /// Class to produce the template output
+ ///
+
+ #line 1 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
+ public partial class AuthorizerSetupParameters : AuthorizerSetupParametersBase
+ {
+#line hidden
+ ///
+ /// Create the template output
+ ///
+ public virtual string TransformText()
+ {
+
+ #line 9 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+
+ // Build the parameter signature for calling the user's method
+ ParameterSignature = string.Join(", ", _model.LambdaMethod.Parameters
+ .Select(p =>
+ {
+ // Pass the same context parameter for ILambdaContext that comes from the generated method.
+ if (p.Type.FullName == TypeFullNames.ILambdaContext)
+ {
+ return "__context__";
+ }
+
+ // Pass the same request parameter for Request Type that comes from the generated method.
+ if (TypeFullNames.Requests.Contains(p.Type.FullName))
+ {
+ return "__request__";
+ }
+
+ return p.Name;
+ }));
+
+ var httpApiAuthorizerAttribute = _model.LambdaMethod.Attributes.FirstOrDefault(att => att.Type.FullName == TypeFullNames.HttpApiAuthorizerAttribute) as AttributeModel;
+ var restApiAuthorizerAttribute = _model.LambdaMethod.Attributes.FirstOrDefault(att => att.Type.FullName == TypeFullNames.RestApiAuthorizerAttribute) as AttributeModel;
+
+ // Determine if the authorizer request type is V2 (has Headers as IDictionary)
+ var isV2Request = httpApiAuthorizerAttribute != null && httpApiAuthorizerAttribute.Data.AuthorizerPayloadFormatVersion == Amazon.Lambda.Annotations.APIGateway.AuthorizerPayloadFormatVersion.V2;
+
+ foreach (var parameter in _model.LambdaMethod.Parameters)
+ {
+ if (parameter.Type.FullName == TypeFullNames.ILambdaContext || TypeFullNames.Requests.Contains(parameter.Type.FullName))
+ {
+ // No action required for ILambdaContext and RequestType, they are passed from the generated method parameter directly to the original method.
+ }
+ else if (parameter.Attributes.Any(att => att.Type.FullName == TypeFullNames.FromServiceAttribute))
+ {
+
+
+ #line default
+ #line hidden
+ this.Write(" var ");
+
+ #line 44 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name));
+
+ #line default
+ #line hidden
+ this.Write(" = scope.ServiceProvider.GetRequiredService<");
+
+ #line 44 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullName));
+
+ #line default
+ #line hidden
+ this.Write(">();\r\n");
+
+ #line 45 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+
+ }
+ else if (parameter.Attributes.Any(att => att.Type.FullName == TypeFullNames.FromHeaderAttribute))
+ {
+ var fromHeaderAttribute =
+ parameter.Attributes.First(att => att.Type.FullName == TypeFullNames.FromHeaderAttribute) as
+ AttributeModel;
+
+ // Use parameter name as key, if Name has not specified explicitly in the attribute definition.
+ var headerKey = fromHeaderAttribute?.Data?.Name ?? parameter.Name;
+
+ // For REST API TOKEN authorizers, API Gateway passes the identity source header value
+ // directly via request.AuthorizationToken (Headers dictionary is null).
+ // For all other authorizer types (HTTP API v2, HTTP API v1, REST API REQUEST),
+ // headers are available in request.Headers as IDictionary.
+ var isRestApiTokenAuthorizer = restApiAuthorizerAttribute != null
+ && restApiAuthorizerAttribute.Data.Type == Amazon.Lambda.Annotations.APIGateway.RestApiAuthorizerType.Token;
+ var identityHeader = restApiAuthorizerAttribute?.Data?.IdentityHeader ?? "Authorization";
+ var isIdentityHeader = isRestApiTokenAuthorizer
+ && string.Equals(headerKey, identityHeader, StringComparison.OrdinalIgnoreCase);
+
+ if (isIdentityHeader)
+ {
+ // REST API TOKEN authorizer: the identity header value is in AuthorizationToken
+
+
+ #line default
+ #line hidden
+ this.Write(" var ");
+
+ #line 70 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name));
+
+ #line default
+ #line hidden
+ this.Write(" = default(");
+
+ #line 70 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullName));
+
+ #line default
+ #line hidden
+ this.Write(");\r\n if (!string.IsNullOrEmpty(__request__.AuthorizationToken))\r\n " +
+ " {\r\n try\r\n {\r\n ");
+
+ #line 75 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name));
+
+ #line default
+ #line hidden
+ this.Write(" = (");
+
+ #line 75 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullName));
+
+ #line default
+ #line hidden
+ this.Write(")Convert.ChangeType(__request__.AuthorizationToken, typeof(");
+
+ #line 75 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullNameWithoutAnnotations));
+
+ #line default
+ #line hidden
+ this.Write(@"));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($""Failed to extract authorization token: {e.Message}"");
+ }
+ }
+");
+
+ #line 82 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+
+ }
+ else
+ {
+ // HTTP API or REST API REQUEST authorizer: headers are in the Headers dictionary
+
+
+ #line default
+ #line hidden
+ this.Write(" var ");
+
+ #line 88 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name));
+
+ #line default
+ #line hidden
+ this.Write(" = default(");
+
+ #line 88 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullName));
+
+ #line default
+ #line hidden
+ this.Write(");\r\n if (__request__.Headers?.Any(x => string.Equals(x.Key, \"");
+
+ #line 89 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(headerKey));
+
+ #line default
+ #line hidden
+ this.Write("\", StringComparison.OrdinalIgnoreCase)) == true)\r\n {\r\n " +
+ "try\r\n {\r\n ");
+
+ #line 93 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name));
+
+ #line default
+ #line hidden
+ this.Write(" = (");
+
+ #line 93 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullName));
+
+ #line default
+ #line hidden
+ this.Write(")Convert.ChangeType(__request__.Headers.First(x => string.Equals(x.Key, \"");
+
+ #line 93 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(headerKey));
+
+ #line default
+ #line hidden
+ this.Write("\", StringComparison.OrdinalIgnoreCase)).Value, typeof(");
+
+ #line 93 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullNameWithoutAnnotations));
+
+ #line default
+ #line hidden
+ this.Write(@"));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($""Failed to extract header '");
+
+ #line 97 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(headerKey));
+
+ #line default
+ #line hidden
+ this.Write("\': {e.Message}\");\r\n }\r\n }\r\n");
+
+ #line 100 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+
+ }
+
+
+ #line default
+ #line hidden
+ this.Write("\r\n");
+
+ #line 104 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+
+ }
+ else if (parameter.Attributes.Any(att => att.Type.FullName == TypeFullNames.FromQueryAttribute))
+ {
+ var fromQueryAttribute = parameter.Attributes.First(att => att.Type.FullName == TypeFullNames.FromQueryAttribute) as AttributeModel;
+ var parameterKey = fromQueryAttribute?.Data?.Name ?? parameter.Name;
+
+
+ #line default
+ #line hidden
+ this.Write(" var ");
+
+ #line 111 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name));
+
+ #line default
+ #line hidden
+ this.Write(" = default(");
+
+ #line 111 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullName));
+
+ #line default
+ #line hidden
+ this.Write(");\r\n if (__request__.QueryStringParameters?.ContainsKey(\"");
+
+ #line 112 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameterKey));
+
+ #line default
+ #line hidden
+ this.Write("\") == true)\r\n {\r\n try\r\n {\r\n " +
+ " ");
+
+ #line 116 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name));
+
+ #line default
+ #line hidden
+ this.Write(" = (");
+
+ #line 116 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullName));
+
+ #line default
+ #line hidden
+ this.Write(")Convert.ChangeType(__request__.QueryStringParameters[\"");
+
+ #line 116 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameterKey));
+
+ #line default
+ #line hidden
+ this.Write("\"], typeof(");
+
+ #line 116 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullNameWithoutAnnotations));
+
+ #line default
+ #line hidden
+ this.Write(@"));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($""Failed to extract query parameter '");
+
+ #line 120 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameterKey));
+
+ #line default
+ #line hidden
+ this.Write("\': {e.Message}\");\r\n }\r\n }\r\n\r\n");
+
+ #line 124 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+
+ }
+ else if (parameter.Attributes.Any(att => att.Type.FullName == TypeFullNames.FromRouteAttribute))
+ {
+ var fromRouteAttribute = parameter.Attributes?.FirstOrDefault(att => att.Type.FullName == TypeFullNames.FromRouteAttribute) as AttributeModel;
+ var routeKey = fromRouteAttribute?.Data?.Name ?? parameter.Name;
+
+
+ #line default
+ #line hidden
+ this.Write(" var ");
+
+ #line 131 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name));
+
+ #line default
+ #line hidden
+ this.Write(" = default(");
+
+ #line 131 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullName));
+
+ #line default
+ #line hidden
+ this.Write(");\r\n if (__request__.PathParameters?.ContainsKey(\"");
+
+ #line 132 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(routeKey));
+
+ #line default
+ #line hidden
+ this.Write("\") == true)\r\n {\r\n try\r\n {\r\n " +
+ " ");
+
+ #line 136 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Name));
+
+ #line default
+ #line hidden
+ this.Write(" = (");
+
+ #line 136 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullName));
+
+ #line default
+ #line hidden
+ this.Write(")Convert.ChangeType(__request__.PathParameters[\"");
+
+ #line 136 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(routeKey));
+
+ #line default
+ #line hidden
+ this.Write("\"], typeof(");
+
+ #line 136 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(parameter.Type.FullNameWithoutAnnotations));
+
+ #line default
+ #line hidden
+ this.Write(@"));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($""Failed to extract route parameter '");
+
+ #line 140 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+ this.Write(this.ToStringHelper.ToStringWithCulture(routeKey));
+
+ #line default
+ #line hidden
+ this.Write("\': {e.Message}\");\r\n }\r\n }\r\n\r\n");
+
+ #line 144 "C:\dev\repos\aws-lambda-dotnet\Libraries\src\Amazon.Lambda.Annotations.SourceGenerator\Templates\AuthorizerSetupParameters.tt"
+
+ }
+ else
+ {
+ throw new NotSupportedException($"{parameter.Name} parameter of type {parameter.Type.FullName} passing is not supported for authorizer functions.");
+ }
+ }
+
+
+ #line default
+ #line hidden
+ return this.GenerationEnvironment.ToString();
+ }
+ }
+
+ #line default
+ #line hidden
+ #region Base class
+ ///
+ /// Base class for this transformation
+ ///
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "18.0.0.0")]
+ public class AuthorizerSetupParametersBase
+ {
+ #region Fields
+ private global::System.Text.StringBuilder generationEnvironmentField;
+ private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField;
+ private global::System.Collections.Generic.List indentLengthsField;
+ private string currentIndentField = "";
+ private bool endsWithNewline;
+ private global::System.Collections.Generic.IDictionary sessionField;
+ #endregion
+ #region Properties
+ ///
+ /// The string builder that generation-time code is using to assemble generated output
+ ///
+ public System.Text.StringBuilder GenerationEnvironment
+ {
+ get
+ {
+ if ((this.generationEnvironmentField == null))
+ {
+ this.generationEnvironmentField = new global::System.Text.StringBuilder();
+ }
+ return this.generationEnvironmentField;
+ }
+ set
+ {
+ this.generationEnvironmentField = value;
+ }
+ }
+ ///
+ /// The error collection for the generation process
+ ///
+ public System.CodeDom.Compiler.CompilerErrorCollection Errors
+ {
+ get
+ {
+ if ((this.errorsField == null))
+ {
+ this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection();
+ }
+ return this.errorsField;
+ }
+ }
+ ///
+ /// A list of the lengths of each indent that was added with PushIndent
+ ///
+ private System.Collections.Generic.List indentLengths
+ {
+ get
+ {
+ if ((this.indentLengthsField == null))
+ {
+ this.indentLengthsField = new global::System.Collections.Generic.List();
+ }
+ return this.indentLengthsField;
+ }
+ }
+ ///
+ /// Gets the current indent we use when adding lines to the output
+ ///
+ public string CurrentIndent
+ {
+ get
+ {
+ return this.currentIndentField;
+ }
+ }
+ ///
+ /// Current transformation session
+ ///
+ public virtual global::System.Collections.Generic.IDictionary Session
+ {
+ get
+ {
+ return this.sessionField;
+ }
+ set
+ {
+ this.sessionField = value;
+ }
+ }
+ #endregion
+ #region Transform-time helpers
+ ///
+ /// Write text directly into the generated output
+ ///
+ public void Write(string textToAppend)
+ {
+ if (string.IsNullOrEmpty(textToAppend))
+ {
+ return;
+ }
+ // If we're starting off, or if the previous text ended with a newline,
+ // we have to append the current indent first.
+ if (((this.GenerationEnvironment.Length == 0)
+ || this.endsWithNewline))
+ {
+ this.GenerationEnvironment.Append(this.currentIndentField);
+ this.endsWithNewline = false;
+ }
+ // Check if the current text ends with a newline
+ if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture))
+ {
+ this.endsWithNewline = true;
+ }
+ // This is an optimization. If the current indent is "", then we don't have to do any
+ // of the more complex stuff further down.
+ if ((this.currentIndentField.Length == 0))
+ {
+ this.GenerationEnvironment.Append(textToAppend);
+ return;
+ }
+ // Everywhere there is a newline in the text, add an indent after it
+ textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField));
+ // If the text ends with a newline, then we should strip off the indent added at the very end
+ // because the appropriate indent will be added when the next time Write() is called
+ if (this.endsWithNewline)
+ {
+ this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length));
+ }
+ else
+ {
+ this.GenerationEnvironment.Append(textToAppend);
+ }
+ }
+ ///
+ /// Write text directly into the generated output
+ ///
+ public void WriteLine(string textToAppend)
+ {
+ this.Write(textToAppend);
+ this.GenerationEnvironment.AppendLine();
+ this.endsWithNewline = true;
+ }
+ ///
+ /// Write formatted text directly into the generated output
+ ///
+ public void Write(string format, params object[] args)
+ {
+ this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
+ }
+ ///
+ /// Write formatted text directly into the generated output
+ ///
+ public void WriteLine(string format, params object[] args)
+ {
+ this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args));
+ }
+ ///
+ /// Raise an error
+ ///
+ public void Error(string message)
+ {
+ System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
+ error.ErrorText = message;
+ this.Errors.Add(error);
+ }
+ ///
+ /// Raise a warning
+ ///
+ public void Warning(string message)
+ {
+ System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError();
+ error.ErrorText = message;
+ error.IsWarning = true;
+ this.Errors.Add(error);
+ }
+ ///
+ /// Increase the indent
+ ///
+ public void PushIndent(string indent)
+ {
+ if ((indent == null))
+ {
+ throw new global::System.ArgumentNullException("indent");
+ }
+ this.currentIndentField = (this.currentIndentField + indent);
+ this.indentLengths.Add(indent.Length);
+ }
+ ///
+ /// Remove the last indent that was added with PushIndent
+ ///
+ public string PopIndent()
+ {
+ string returnValue = "";
+ if ((this.indentLengths.Count > 0))
+ {
+ int indentLength = this.indentLengths[(this.indentLengths.Count - 1)];
+ this.indentLengths.RemoveAt((this.indentLengths.Count - 1));
+ if ((indentLength > 0))
+ {
+ returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength));
+ this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength));
+ }
+ }
+ return returnValue;
+ }
+ ///
+ /// Remove any indentation
+ ///
+ public void ClearIndent()
+ {
+ this.indentLengths.Clear();
+ this.currentIndentField = "";
+ }
+ #endregion
+ #region ToString Helpers
+ ///
+ /// Utility class to produce culture-oriented representation of an object as a string.
+ ///
+ public class ToStringInstanceHelper
+ {
+ private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture;
+ ///
+ /// Gets or sets format provider to be used by ToStringWithCulture method.
+ ///
+ public System.IFormatProvider FormatProvider
+ {
+ get
+ {
+ return this.formatProviderField ;
+ }
+ set
+ {
+ if ((value != null))
+ {
+ this.formatProviderField = value;
+ }
+ }
+ }
+ ///
+ /// This is called from the compile/run appdomain to convert objects within an expression block to a string
+ ///
+ public string ToStringWithCulture(object objectToConvert)
+ {
+ if ((objectToConvert == null))
+ {
+ throw new global::System.ArgumentNullException("objectToConvert");
+ }
+ System.Type t = objectToConvert.GetType();
+ System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] {
+ typeof(System.IFormatProvider)});
+ if ((method == null))
+ {
+ return objectToConvert.ToString();
+ }
+ else
+ {
+ return ((string)(method.Invoke(objectToConvert, new object[] {
+ this.formatProviderField })));
+ }
+ }
+ }
+ private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper();
+ ///
+ /// Helper to produce culture-oriented representation of an object as a string
+ ///
+ public ToStringInstanceHelper ToStringHelper
+ {
+ get
+ {
+ return this.toStringHelperField;
+ }
+ }
+ #endregion
+ }
+ #endregion
+}
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerSetupParameters.tt b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerSetupParameters.tt
new file mode 100644
index 000000000..efd4f9a59
--- /dev/null
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerSetupParameters.tt
@@ -0,0 +1,151 @@
+<#@ template language="C#" #>
+<#@ assembly name="System.Core" #>
+<#@ import namespace="System.Linq" #>
+<#@ import namespace="System.Text" #>
+<#@ import namespace="System.Collections.Generic" #>
+<#@ import namespace="Amazon.Lambda.Annotations.SourceGenerator.Extensions" #>
+<#@ import namespace="Amazon.Lambda.Annotations.SourceGenerator.Models" #>
+<#@ import namespace="Amazon.Lambda.Annotations.SourceGenerator.Models.Attributes" #>
+<#
+ // Build the parameter signature for calling the user's method
+ ParameterSignature = string.Join(", ", _model.LambdaMethod.Parameters
+ .Select(p =>
+ {
+ // Pass the same context parameter for ILambdaContext that comes from the generated method.
+ if (p.Type.FullName == TypeFullNames.ILambdaContext)
+ {
+ return "__context__";
+ }
+
+ // Pass the same request parameter for Request Type that comes from the generated method.
+ if (TypeFullNames.Requests.Contains(p.Type.FullName))
+ {
+ return "__request__";
+ }
+
+ return p.Name;
+ }));
+
+ var httpApiAuthorizerAttribute = _model.LambdaMethod.Attributes.FirstOrDefault(att => att.Type.FullName == TypeFullNames.HttpApiAuthorizerAttribute) as AttributeModel;
+ var restApiAuthorizerAttribute = _model.LambdaMethod.Attributes.FirstOrDefault(att => att.Type.FullName == TypeFullNames.RestApiAuthorizerAttribute) as AttributeModel;
+
+ // Determine if the authorizer request type is V2 (has Headers as IDictionary)
+ var isV2Request = httpApiAuthorizerAttribute != null && httpApiAuthorizerAttribute.Data.AuthorizerPayloadFormatVersion == Amazon.Lambda.Annotations.APIGateway.AuthorizerPayloadFormatVersion.V2;
+
+ foreach (var parameter in _model.LambdaMethod.Parameters)
+ {
+ if (parameter.Type.FullName == TypeFullNames.ILambdaContext || TypeFullNames.Requests.Contains(parameter.Type.FullName))
+ {
+ // No action required for ILambdaContext and RequestType, they are passed from the generated method parameter directly to the original method.
+ }
+ else if (parameter.Attributes.Any(att => att.Type.FullName == TypeFullNames.FromServiceAttribute))
+ {
+#>
+ var <#= parameter.Name #> = scope.ServiceProvider.GetRequiredService<<#= parameter.Type.FullName #>>();
+<#
+ }
+ else if (parameter.Attributes.Any(att => att.Type.FullName == TypeFullNames.FromHeaderAttribute))
+ {
+ var fromHeaderAttribute =
+ parameter.Attributes.First(att => att.Type.FullName == TypeFullNames.FromHeaderAttribute) as
+ AttributeModel;
+
+ // Use parameter name as key, if Name has not specified explicitly in the attribute definition.
+ var headerKey = fromHeaderAttribute?.Data?.Name ?? parameter.Name;
+
+ // For REST API TOKEN authorizers, API Gateway passes the identity source header value
+ // directly via request.AuthorizationToken (Headers dictionary is null).
+ // For all other authorizer types (HTTP API v2, HTTP API v1, REST API REQUEST),
+ // headers are available in request.Headers as IDictionary.
+ var isRestApiTokenAuthorizer = restApiAuthorizerAttribute != null
+ && restApiAuthorizerAttribute.Data.Type == Amazon.Lambda.Annotations.APIGateway.RestApiAuthorizerType.Token;
+ var identityHeader = restApiAuthorizerAttribute?.Data?.IdentityHeader ?? "Authorization";
+ var isIdentityHeader = isRestApiTokenAuthorizer
+ && string.Equals(headerKey, identityHeader, StringComparison.OrdinalIgnoreCase);
+
+ if (isIdentityHeader)
+ {
+ // REST API TOKEN authorizer: the identity header value is in AuthorizationToken
+#>
+ var <#= parameter.Name #> = default(<#= parameter.Type.FullName #>);
+ if (!string.IsNullOrEmpty(__request__.AuthorizationToken))
+ {
+ try
+ {
+ <#= parameter.Name #> = (<#= parameter.Type.FullName #>)Convert.ChangeType(__request__.AuthorizationToken, typeof(<#= parameter.Type.FullNameWithoutAnnotations #>));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($"Failed to extract authorization token: {e.Message}");
+ }
+ }
+<#
+ }
+ else
+ {
+ // HTTP API or REST API REQUEST authorizer: headers are in the Headers dictionary
+#>
+ var <#= parameter.Name #> = default(<#= parameter.Type.FullName #>);
+ if (__request__.Headers?.Any(x => string.Equals(x.Key, "<#= headerKey #>", StringComparison.OrdinalIgnoreCase)) == true)
+ {
+ try
+ {
+ <#= parameter.Name #> = (<#= parameter.Type.FullName #>)Convert.ChangeType(__request__.Headers.First(x => string.Equals(x.Key, "<#= headerKey #>", StringComparison.OrdinalIgnoreCase)).Value, typeof(<#= parameter.Type.FullNameWithoutAnnotations #>));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($"Failed to extract header '<#= headerKey #>': {e.Message}");
+ }
+ }
+<#
+ }
+#>
+
+<#
+ }
+ else if (parameter.Attributes.Any(att => att.Type.FullName == TypeFullNames.FromQueryAttribute))
+ {
+ var fromQueryAttribute = parameter.Attributes.First(att => att.Type.FullName == TypeFullNames.FromQueryAttribute) as AttributeModel;
+ var parameterKey = fromQueryAttribute?.Data?.Name ?? parameter.Name;
+#>
+ var <#= parameter.Name #> = default(<#= parameter.Type.FullName #>);
+ if (__request__.QueryStringParameters?.ContainsKey("<#= parameterKey #>") == true)
+ {
+ try
+ {
+ <#= parameter.Name #> = (<#= parameter.Type.FullName #>)Convert.ChangeType(__request__.QueryStringParameters["<#= parameterKey #>"], typeof(<#= parameter.Type.FullNameWithoutAnnotations #>));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($"Failed to extract query parameter '<#= parameterKey #>': {e.Message}");
+ }
+ }
+
+<#
+ }
+ else if (parameter.Attributes.Any(att => att.Type.FullName == TypeFullNames.FromRouteAttribute))
+ {
+ var fromRouteAttribute = parameter.Attributes?.FirstOrDefault(att => att.Type.FullName == TypeFullNames.FromRouteAttribute) as AttributeModel;
+ var routeKey = fromRouteAttribute?.Data?.Name ?? parameter.Name;
+#>
+ var <#= parameter.Name #> = default(<#= parameter.Type.FullName #>);
+ if (__request__.PathParameters?.ContainsKey("<#= routeKey #>") == true)
+ {
+ try
+ {
+ <#= parameter.Name #> = (<#= parameter.Type.FullName #>)Convert.ChangeType(__request__.PathParameters["<#= routeKey #>"], typeof(<#= parameter.Type.FullNameWithoutAnnotations #>));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($"Failed to extract route parameter '<#= routeKey #>': {e.Message}");
+ }
+ }
+
+<#
+ }
+ else
+ {
+ throw new NotSupportedException($"{parameter.Name} parameter of type {parameter.Type.FullName} passing is not supported for authorizer functions.");
+ }
+ }
+#>
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerSetupParametersCode.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerSetupParametersCode.cs
new file mode 100644
index 000000000..412d65b9c
--- /dev/null
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/AuthorizerSetupParametersCode.cs
@@ -0,0 +1,16 @@
+using Amazon.Lambda.Annotations.SourceGenerator.Models;
+
+namespace Amazon.Lambda.Annotations.SourceGenerator.Templates
+{
+ public partial class AuthorizerSetupParameters
+ {
+ private readonly LambdaFunctionModel _model;
+
+ public string ParameterSignature { get; set; }
+
+ public AuthorizerSetupParameters(LambdaFunctionModel model)
+ {
+ _model = model;
+ }
+ }
+}
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/LambdaFunctionTemplate.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/LambdaFunctionTemplate.cs
index ced24e1d1..2706b7b20 100644
--- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/LambdaFunctionTemplate.cs
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/LambdaFunctionTemplate.cs
@@ -176,13 +176,19 @@ public virtual string TransformText()
}
- if (_model.LambdaMethod.Events.Contains(EventType.API))
+ if (_model.LambdaMethod.Events.Contains(EventType.Authorizer))
+ {
+ var authorizerParameters = new AuthorizerSetupParameters(_model);
+ this.Write(authorizerParameters.TransformText());
+ this.Write(new AuthorizerInvoke(_model, authorizerParameters.ParameterSignature).TransformText());
+ }
+ else if (_model.LambdaMethod.Events.Contains(EventType.API))
{
var apiParameters = new APIGatewaySetupParameters(_model);
this.Write(apiParameters.TransformText());
this.Write(new APIGatewayInvoke(_model, apiParameters.ParameterSignature).TransformText());
}
- // Currently we only support 2 event types - APIGatewayEvents and SQSEvents.
+ // Currently we support 3 event types - Authorizer, APIGatewayEvents, and SQSEvents.
// Since SQSEvents does not require any special code generation, the generated method body will be same as when the Lambda method contains no events.
else
{
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/LambdaFunctionTemplate.tt b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/LambdaFunctionTemplate.tt
index e32430f70..44d2212f5 100644
--- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/LambdaFunctionTemplate.tt
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Templates/LambdaFunctionTemplate.tt
@@ -54,13 +54,19 @@ this.Write(new FieldsAndConstructor(_model).TransformText());
<#
}
- if (_model.LambdaMethod.Events.Contains(EventType.API))
+ if (_model.LambdaMethod.Events.Contains(EventType.Authorizer))
+ {
+ var authorizerParameters = new AuthorizerSetupParameters(_model);
+ this.Write(authorizerParameters.TransformText());
+ this.Write(new AuthorizerInvoke(_model, authorizerParameters.ParameterSignature).TransformText());
+ }
+ else if (_model.LambdaMethod.Events.Contains(EventType.API))
{
var apiParameters = new APIGatewaySetupParameters(_model);
this.Write(apiParameters.TransformText());
this.Write(new APIGatewayInvoke(_model, apiParameters.ParameterSignature).TransformText());
}
- // Currently we only support 2 event types - APIGatewayEvents and SQSEvents.
+ // Currently we support 3 event types - Authorizer, APIGatewayEvents, and SQSEvents.
// Since SQSEvents does not require any special code generation, the generated method body will be same as when the Lambda method contains no events.
else
{
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/TypeFullNames.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/TypeFullNames.cs
index 56582a2e7..39d01a03a 100644
--- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/TypeFullNames.cs
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/TypeFullNames.cs
@@ -20,6 +20,7 @@ public static class TypeFullNames
public const string APIGatewayHttpApiV2ProxyResponse = "Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse";
public const string IHttpResult = "Amazon.Lambda.Annotations.APIGateway.IHttpResult";
+ public const string IAuthorizerResult = "Amazon.Lambda.Annotations.APIGateway.IAuthorizerResult";
public const string LambdaFunctionAttribute = "Amazon.Lambda.Annotations.LambdaFunctionAttribute";
public const string FromServiceAttribute = "Amazon.Lambda.Annotations.FromServicesAttribute";
@@ -36,6 +37,11 @@ public static class TypeFullNames
public const string HttpApiAuthorizerAttribute = "Amazon.Lambda.Annotations.APIGateway.HttpApiAuthorizerAttribute";
public const string RestApiAuthorizerAttribute = "Amazon.Lambda.Annotations.APIGateway.RestApiAuthorizerAttribute";
+ public const string APIGatewayCustomAuthorizerV2Request = "Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerV2Request";
+ public const string APIGatewayCustomAuthorizerRequest = "Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerRequest";
+ public const string APIGatewayCustomAuthorizerV2SimpleResponse = "Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerV2SimpleResponse";
+ public const string APIGatewayCustomAuthorizerResponse = "Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerResponse";
+
public const string SQSEvent = "Amazon.Lambda.SQSEvents.SQSEvent";
public const string SQSBatchResponse = "Amazon.Lambda.SQSEvents.SQSBatchResponse";
public const string SQSEventAttribute = "Amazon.Lambda.Annotations.SQS.SQSEventAttribute";
@@ -48,7 +54,9 @@ public static class TypeFullNames
public static HashSet Requests = new HashSet
{
APIGatewayProxyRequest,
- APIGatewayHttpApiV2ProxyRequest
+ APIGatewayHttpApiV2ProxyRequest,
+ APIGatewayCustomAuthorizerV2Request,
+ APIGatewayCustomAuthorizerRequest
};
public static HashSet Events = new HashSet
diff --git a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Validation/LambdaFunctionValidator.cs b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Validation/LambdaFunctionValidator.cs
index 8b305e7cf..470e80aca 100644
--- a/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Validation/LambdaFunctionValidator.cs
+++ b/Libraries/src/Amazon.Lambda.Annotations.SourceGenerator/Validation/LambdaFunctionValidator.cs
@@ -65,8 +65,9 @@ internal static bool ValidateFunction(GeneratorExecutionContext context, IMethod
internal static bool ValidateDependencies(GeneratorExecutionContext context, IMethodSymbol lambdaMethodSymbol, Location methodLocation, DiagnosticReporter diagnosticReporter)
{
- // Check for references to "Amazon.Lambda.APIGatewayEvents" if the Lambda method is annotated with RestApi or HttpApi attributes.
- if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAttribute) || lambdaMethodSymbol.HasAttribute(context, TypeFullNames.HttpApiAttribute))
+ // Check for references to "Amazon.Lambda.APIGatewayEvents" if the Lambda method is annotated with RestApi, HttpApi, or authorizer attributes.
+ if (lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAttribute) || lambdaMethodSymbol.HasAttribute(context, TypeFullNames.HttpApiAttribute)
+ || lambdaMethodSymbol.HasAttribute(context, TypeFullNames.HttpApiAuthorizerAttribute) || lambdaMethodSymbol.HasAttribute(context, TypeFullNames.RestApiAuthorizerAttribute))
{
if (context.Compilation.ReferencedAssemblyNames.FirstOrDefault(x => x.Name == "Amazon.Lambda.APIGatewayEvents") == null)
{
@@ -90,8 +91,10 @@ internal static bool ValidateDependencies(GeneratorExecutionContext context, IMe
private static void ValidateApiGatewayEvents(LambdaFunctionModel lambdaFunctionModel, Location methodLocation, List diagnostics)
{
- // If the method does not contain any APIGatewayEvents, then it cannot return IHttpResults and can also not have parameters that are annotated with HTTP API attributes
- if (!lambdaFunctionModel.LambdaMethod.Events.Contains(EventType.API))
+ // If the method does not contain any APIGatewayEvents or Authorizer events, then it cannot return IHttpResults
+ // and can also not have parameters that are annotated with HTTP API attributes.
+ // Authorizer functions also support FromHeader, FromQuery, FromRoute attributes.
+ if (!lambdaFunctionModel.LambdaMethod.Events.Contains(EventType.API) && !lambdaFunctionModel.LambdaMethod.Events.Contains(EventType.Authorizer))
{
if (lambdaFunctionModel.LambdaMethod.ReturnsIHttpResults)
{
@@ -113,6 +116,12 @@ private static void ValidateApiGatewayEvents(LambdaFunctionModel lambdaFunctionM
return;
}
+ // If this is an Authorizer event, skip the API Gateway-specific parameter validation below
+ if (lambdaFunctionModel.LambdaMethod.Events.Contains(EventType.Authorizer))
+ {
+ return;
+ }
+
// Validate FromRoute, FromQuery and FromHeader parameters
foreach (var parameter in lambdaFunctionModel.LambdaMethod.Parameters)
{
diff --git a/Libraries/src/Amazon.Lambda.Annotations/APIGateway/AuthorizerResultSerializationOptions.cs b/Libraries/src/Amazon.Lambda.Annotations/APIGateway/AuthorizerResultSerializationOptions.cs
new file mode 100644
index 000000000..f7fc06601
--- /dev/null
+++ b/Libraries/src/Amazon.Lambda.Annotations/APIGateway/AuthorizerResultSerializationOptions.cs
@@ -0,0 +1,47 @@
+namespace Amazon.Lambda.Annotations.APIGateway
+{
+ ///
+ /// Options used by to serialize into the correct API Gateway authorizer response format.
+ /// These options are set by the generated handler code based on the authorizer attribute configuration.
+ ///
+ public class AuthorizerResultSerializationOptions
+ {
+ ///
+ /// The authorizer response format to serialize into.
+ ///
+ public enum AuthorizerFormat
+ {
+ ///
+ /// HTTP API simple response format (IsAuthorized: true/false with optional context).
+ /// Used when is true.
+ /// Produces APIGatewayCustomAuthorizerV2SimpleResponse.
+ ///
+ HttpApiSimple,
+
+ ///
+ /// HTTP API IAM policy response format.
+ /// Used when is false.
+ /// Produces APIGatewayCustomAuthorizerResponse.
+ ///
+ HttpApiIamPolicy,
+
+ ///
+ /// REST API authorizer response format (always IAM policy-based).
+ /// Produces APIGatewayCustomAuthorizerResponse.
+ ///
+ RestApi
+ }
+
+ ///
+ /// The authorizer response format to use for serialization.
+ ///
+ public AuthorizerFormat Format { get; set; }
+
+ ///
+ /// The method ARN from the authorizer request. Used to construct IAM policy documents
+ /// for REST API and HTTP API IAM policy response formats.
+ /// For HTTP API simple responses, this value is not used.
+ ///
+ public string MethodArn { get; set; }
+ }
+}
diff --git a/Libraries/src/Amazon.Lambda.Annotations/APIGateway/AuthorizerResults.cs b/Libraries/src/Amazon.Lambda.Annotations/APIGateway/AuthorizerResults.cs
new file mode 100644
index 000000000..b068e9c08
--- /dev/null
+++ b/Libraries/src/Amazon.Lambda.Annotations/APIGateway/AuthorizerResults.cs
@@ -0,0 +1,231 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+#if !NETSTANDARD2_0
+using System.Text.Json;
+using System.Text.Json.Serialization;
+#endif
+
+namespace Amazon.Lambda.Annotations.APIGateway
+{
+ ///
+ /// Implementation class for . Use the static and
+ /// factory methods to create instances, then optionally chain and
+ /// to add context values and a principal ID.
+ ///
+ ///
+ ///
+ /// For HTTP API simple authorizers, the result is serialized to:
+ /// { "isAuthorized": true/false, "context": { ... } }
+ ///
+ ///
+ /// For REST API and HTTP API IAM policy authorizers, the result is serialized to an IAM policy document
+ /// with an Allow or Deny effect. The policy resource ARN is derived from the request's MethodArn.
+ ///
+ ///
+ ///
+ ///
+ /// // Simple allow with context
+ /// return AuthorizerResults.Allow()
+ /// .WithContext("userId", "user-123")
+ /// .WithContext("role", "admin");
+ ///
+ /// // Simple deny
+ /// return AuthorizerResults.Deny();
+ ///
+ /// // REST API with principal ID
+ /// return AuthorizerResults.Allow()
+ /// .WithPrincipalId("user-123")
+ /// .WithContext("tenantId", "42");
+ ///
+ ///
+ public class AuthorizerResults : IAuthorizerResult
+ {
+ private AuthorizerResults(bool isAuthorized)
+ {
+ IsAuthorized = isAuthorized;
+ }
+
+ ///
+ public bool IsAuthorized { get; }
+
+ ///
+ public string PrincipalId { get; private set; }
+
+ ///
+ public IDictionary Context { get; private set; }
+
+ ///
+ /// Creates an that allows the request.
+ ///
+ /// An authorized result instance.
+ public static IAuthorizerResult Allow()
+ {
+ return new AuthorizerResults(true);
+ }
+
+ ///
+ /// Creates an that denies the request.
+ ///
+ /// A denied result instance.
+ public static IAuthorizerResult Deny()
+ {
+ return new AuthorizerResults(false);
+ }
+
+ ///
+ public IAuthorizerResult WithContext(string key, object value)
+ {
+ if (Context == null)
+ {
+ Context = new Dictionary();
+ }
+
+ Context[key] = value;
+ return this;
+ }
+
+ ///
+ public IAuthorizerResult WithPrincipalId(string principalId)
+ {
+ PrincipalId = principalId;
+ return this;
+ }
+
+ ///
+ /// Serializes the authorizer result into the correct API Gateway response format as a JSON stream.
+ /// This method is called by the generated Lambda handler code.
+ ///
+ /// Serialization options that determine the output format.
+ /// The serialized response object appropriate for the authorizer format.
+ public object Serialize(AuthorizerResultSerializationOptions options)
+ {
+#if NETSTANDARD2_0
+ throw new NotImplementedException();
+#else
+ switch (options.Format)
+ {
+ case AuthorizerResultSerializationOptions.AuthorizerFormat.HttpApiSimple:
+ return SerializeSimpleResponse();
+
+ case AuthorizerResultSerializationOptions.AuthorizerFormat.HttpApiIamPolicy:
+ case AuthorizerResultSerializationOptions.AuthorizerFormat.RestApi:
+ return SerializeIamPolicyResponse(options.MethodArn);
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(options), $"Unsupported authorizer format: {options.Format}");
+ }
+#endif
+ }
+
+#if !NETSTANDARD2_0
+ private Stream SerializeSimpleResponse()
+ {
+ var response = new SimpleAuthorizerResponse
+ {
+ IsAuthorized = IsAuthorized,
+ Context = Context != null ? new Dictionary(Context) : null
+ };
+
+ var stream = new MemoryStream();
+ JsonSerializer.Serialize(stream, response, typeof(SimpleAuthorizerResponse), AuthorizerResponseJsonSerializerContext.Default);
+ stream.Position = 0;
+ return stream;
+ }
+
+ private Stream SerializeIamPolicyResponse(string methodArn)
+ {
+ var effect = IsAuthorized ? "Allow" : "Deny";
+ var resource = methodArn ?? "*";
+
+ var response = new IamPolicyAuthorizerResponse
+ {
+ PrincipalId = PrincipalId ?? "user",
+ PolicyDocument = new PolicyDocument
+ {
+ Version = "2012-10-17",
+ Statement = new List
+ {
+ new PolicyStatement
+ {
+ Action = "execute-api:Invoke",
+ Effect = effect,
+ Resource = resource
+ }
+ }
+ },
+ Context = Context != null ? new Dictionary(Context) : null
+ };
+
+ var stream = new MemoryStream();
+ JsonSerializer.Serialize(stream, response, typeof(IamPolicyAuthorizerResponse), AuthorizerResponseJsonSerializerContext.Default);
+ stream.Position = 0;
+ return stream;
+ }
+
+ // Internal response types that mirror the API Gateway authorizer response structures.
+ // The Annotations library cannot take a dependency on Amazon.Lambda.APIGatewayEvents,
+ // so it defines its own serializable types (same pattern as HttpResults).
+
+ internal class SimpleAuthorizerResponse
+ {
+ [JsonPropertyName("isAuthorized")]
+ public bool IsAuthorized { get; set; }
+
+ [JsonPropertyName("context")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public Dictionary Context { get; set; }
+ }
+
+ internal class IamPolicyAuthorizerResponse
+ {
+ [JsonPropertyName("principalId")]
+ public string PrincipalId { get; set; }
+
+ [JsonPropertyName("policyDocument")]
+ public PolicyDocument PolicyDocument { get; set; }
+
+ [JsonPropertyName("context")]
+ [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
+ public Dictionary Context { get; set; }
+ }
+
+ internal class PolicyDocument
+ {
+ [JsonPropertyName("Version")]
+ public string Version { get; set; }
+
+ [JsonPropertyName("Statement")]
+ public List Statement { get; set; }
+ }
+
+ internal class PolicyStatement
+ {
+ [JsonPropertyName("Action")]
+ public string Action { get; set; }
+
+ [JsonPropertyName("Effect")]
+ public string Effect { get; set; }
+
+ [JsonPropertyName("Resource")]
+ public string Resource { get; set; }
+ }
+#endif
+ }
+
+#if !NETSTANDARD2_0
+ [JsonSerializable(typeof(AuthorizerResults.SimpleAuthorizerResponse))]
+ [JsonSerializable(typeof(AuthorizerResults.IamPolicyAuthorizerResponse))]
+ [JsonSerializable(typeof(Dictionary))]
+ [JsonSerializable(typeof(string))]
+ [JsonSerializable(typeof(int))]
+ [JsonSerializable(typeof(bool))]
+ [JsonSerializable(typeof(double))]
+ [JsonSerializable(typeof(long))]
+ [JsonSerializable(typeof(float))]
+ internal partial class AuthorizerResponseJsonSerializerContext : JsonSerializerContext
+ {
+ }
+#endif
+}
diff --git a/Libraries/src/Amazon.Lambda.Annotations/APIGateway/IAuthorizerResult.cs b/Libraries/src/Amazon.Lambda.Annotations/APIGateway/IAuthorizerResult.cs
new file mode 100644
index 000000000..462908972
--- /dev/null
+++ b/Libraries/src/Amazon.Lambda.Annotations/APIGateway/IAuthorizerResult.cs
@@ -0,0 +1,70 @@
+using System.Collections.Generic;
+
+namespace Amazon.Lambda.Annotations.APIGateway
+{
+ ///
+ /// Represents the result of a Lambda authorizer function. Similar to how abstracts
+ /// API Gateway proxy responses, this interface abstracts the authorizer response format.
+ ///
+ /// The source generator will serialize this into the correct API Gateway response type based on the
+ /// authorizer attribute configuration (HttpApiAuthorizer with simple/IAM responses, or RestApiAuthorizer).
+ ///
+ ///
+ /// Users should use the factory class to create instances of this interface.
+ ///
+ ///
+ ///
+ /// [LambdaFunction]
+ /// [HttpApiAuthorizer(EnableSimpleResponses = true)]
+ /// public IAuthorizerResult Authorize([FromHeader("Authorization")] string auth, ILambdaContext context)
+ /// {
+ /// if (IsValid(auth))
+ /// return AuthorizerResults.Allow()
+ /// .WithContext("userId", "user-123");
+ /// return AuthorizerResults.Deny();
+ /// }
+ ///
+ ///
+ public interface IAuthorizerResult
+ {
+ ///
+ /// Whether the request is authorized.
+ ///
+ bool IsAuthorized { get; }
+
+ ///
+ /// The principal ID for the caller. Used primarily by REST API authorizers.
+ /// For HTTP API simple authorizers, this value is ignored.
+ ///
+ string PrincipalId { get; }
+
+ ///
+ /// Context key-value pairs that are passed to downstream Lambda functions.
+ /// These values can be accessed using in protected endpoints.
+ ///
+ IDictionary Context { get; }
+
+ ///
+ /// Add a context key-value pair that will be passed to downstream Lambda functions.
+ ///
+ /// The context key name
+ /// The context value
+ /// The same instance to allow fluent call pattern.
+ IAuthorizerResult WithContext(string key, object value);
+
+ ///
+ /// Set the principal ID for the caller. Used by REST API and HTTP API IAM policy authorizers.
+ ///
+ /// The principal identifier
+ /// The same instance to allow fluent call pattern.
+ IAuthorizerResult WithPrincipalId(string principalId);
+
+ ///
+ /// Serialize the authorizer result into the correct API Gateway response format.
+ /// This is called by the generated Lambda handler code.
+ ///
+ /// Serialization options that determine the output format
+ /// The serialized response object (type depends on the authorizer format)
+ object Serialize(AuthorizerResultSerializationOptions options);
+ }
+}
diff --git a/Libraries/src/Amazon.Lambda.Annotations/README.md b/Libraries/src/Amazon.Lambda.Annotations/README.md
index 720b1e9e9..75dfaac23 100644
--- a/Libraries/src/Amazon.Lambda.Annotations/README.md
+++ b/Libraries/src/Amazon.Lambda.Annotations/README.md
@@ -23,6 +23,7 @@ Topics:
- [HTTP API Authorizer](#http-api-authorizer)
- [REST API Authorizer](#rest-api-authorizer)
- [Authorizer Attribute Properties](#authorizer-attribute-properties)
+ - [Simplified Authorizer with IAuthorizerResult](#simplified-authorizer-with-iauthorizerresult)
- [Getting build information](#getting-build-information)
- [Lambda .NET Attributes Reference](#lambda-net-attributes-reference)
- [Event Attributes](#event-attributes)
@@ -1068,6 +1069,98 @@ public object ExternalEndpoint(
}
```
+### Simplified Authorizer with IAuthorizerResult
+
+As an alternative to working with raw API Gateway authorizer request and response types, authorizer functions can use `IAuthorizerResult` and the `AuthorizerResults` factory class for a simplified developer experience. This follows the same pattern as `IHttpResult`/`HttpResults` for API Gateway endpoint responses.
+
+With this pattern:
+- **`[FromHeader]`, `[FromQuery]`, and `[FromRoute]`** attributes can be used on authorizer function parameters to extract values from the authorizer request, just like they work on `[HttpApi]`/`[RestApi]` endpoint functions.
+- **`AuthorizerResults.Allow()`** and **`AuthorizerResults.Deny()`** replace manual construction of API Gateway response objects and IAM policy documents.
+- **`.WithContext(key, value)`** adds context values that downstream functions can access via `[FromCustomAuthorizer]`.
+- **`.WithPrincipalId(id)`** sets the principal ID (used by REST API and HTTP API IAM policy authorizers).
+
+The source generator automatically handles serialization to the correct API Gateway response format based on the authorizer attribute configuration.
+
+**HTTP API authorizer using IAuthorizerResult:**
+
+```csharp
+[LambdaFunction]
+[HttpApiAuthorizer(
+ EnableSimpleResponses = true,
+ AuthorizerPayloadFormatVersion = AuthorizerPayloadFormatVersion.V2)]
+public IAuthorizerResult SimpleHttpApiAuthorize(
+ [FromHeader(Name = "Authorization")] string authorization,
+ ILambdaContext context)
+{
+ if (string.IsNullOrEmpty(authorization))
+ return AuthorizerResults.Deny();
+
+ if (IsValidToken(authorization))
+ {
+ return AuthorizerResults.Allow()
+ .WithContext("userId", "user-12345")
+ .WithContext("email", "test@example.com");
+ }
+
+ return AuthorizerResults.Deny();
+}
+```
+
+**REST API authorizer using IAuthorizerResult:**
+
+For REST API authorizers, the source generator automatically constructs the IAM policy document from the `Allow()`/`Deny()` result. Use `WithPrincipalId()` to set the caller's principal ID.
+
+```csharp
+[LambdaFunction]
+[RestApiAuthorizer(
+ Type = RestApiAuthorizerType.Token,
+ IdentityHeader = "Authorization")]
+public IAuthorizerResult SimpleRestApiAuthorize(
+ [FromHeader(Name = "Authorization")] string authorization,
+ ILambdaContext context)
+{
+ if (string.IsNullOrEmpty(authorization))
+ return AuthorizerResults.Deny();
+
+ if (IsValidToken(authorization))
+ {
+ return AuthorizerResults.Allow()
+ .WithPrincipalId("user-12345")
+ .WithContext("userId", "user-12345")
+ .WithContext("email", "test@example.com");
+ }
+
+ return AuthorizerResults.Deny();
+}
+```
+
+Protected endpoints work the same way regardless of whether the authorizer uses raw types or `IAuthorizerResult`:
+
+```csharp
+[LambdaFunction]
+[HttpApi(LambdaHttpMethod.Get, "/api/user-info", Authorizer = nameof(SimpleHttpApiAuthorize))]
+public object GetUserInfo(
+ [FromCustomAuthorizer(Name = "userId")] string userId,
+ [FromCustomAuthorizer(Name = "email")] string email,
+ ILambdaContext context)
+{
+ return new { UserId = userId, Email = email };
+}
+```
+
+**`AuthorizerResults` API reference:**
+
+| Method | Description |
+|--------|-------------|
+| `AuthorizerResults.Allow()` | Creates a result that authorizes the request. |
+| `AuthorizerResults.Deny()` | Creates a result that denies the request. |
+| `.WithContext(key, value)` | Adds a context key-value pair passed to downstream functions (accessible via `[FromCustomAuthorizer]`). |
+| `.WithPrincipalId(id)` | Sets the principal ID for the caller (used by REST API and HTTP API IAM policy authorizers). |
+
+Async return types (`Task`) are also supported.
+
+> **Backwards compatibility:** Both patterns — raw API Gateway types and `IAuthorizerResult` — are fully supported and can coexist in the same project. The source generator detects which pattern is used based on the return type.
+
## Getting build information
The source generator integrates with MSBuild's compiler error and warning reporting when there are problems generating the boiler plate code.
@@ -1110,11 +1203,11 @@ parameter to the `LambdaFunction` must be the event object and the event source
### Parameter Attributes
* FromHeader
- * Map method parameter to HTTP header value
+ * Map method parameter to HTTP header value. Also supported on authorizer functions (see [Simplified Authorizer with IAuthorizerResult](#simplified-authorizer-with-iauthorizerresult)).
* FromQuery
- * Map method parameter to query string parameter
+ * Map method parameter to query string parameter. Also supported on authorizer functions.
* FromRoute
- * Map method parameter to resource path segment
+ * Map method parameter to resource path segment. Also supported on authorizer functions.
* FromBody
* Map method parameter to HTTP request body. If parameter is a complex type then request body will be assumed to be JSON and deserialized into the type.
* FromServices
@@ -1141,6 +1234,8 @@ public async Task ProtectedEndpoint(
The attributes `RestApi` or `HttpApi` configure a `LambdaFunction` method to use API Gateway as the event source for the function. By default these methods return an
HTTP status code of 200. To customize the HTTP response, including adding HTTP headers, the method signature must return an `Amazon.Lambda.Annotations.APIGateway.IHttpResult`
or `Task`.
+
+Similarly, authorizer functions can return `Amazon.Lambda.Annotations.APIGateway.IAuthorizerResult` or `Task` to use the simplified authorizer response pattern instead of raw API Gateway types. See the [Simplified Authorizer with IAuthorizerResult](#simplified-authorizer-with-iauthorizerresult) section for details.
The `Amazon.Lambda.Annotations.APIGateway.HttpResults` class contains static methods for creating an instance of `IHttpResult` with the appropriate HTTP status code and headers.
The example below shows how to return a HTTP status code 404 with a response body and custom header.
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_HttpApiAuthorizeV1_Generated.g.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_HttpApiAuthorizeV1_Generated.g.cs
index 2611b8c62..6f104bec7 100644
--- a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_HttpApiAuthorizeV1_Generated.g.cs
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_HttpApiAuthorizeV1_Generated.g.cs
@@ -30,12 +30,13 @@ public AuthorizerFunction_HttpApiAuthorizeV1_Generated()
///
/// The generated Lambda function handler for
///
- /// The request object that will be processed by the Lambda function handler.
+ /// The API Gateway authorizer request object that will be processed by the Lambda function handler.
/// The ILambdaContext that provides methods for logging and describing the Lambda environment.
/// Result of the Lambda function execution
public Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerResponse HttpApiAuthorizeV1(Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerRequest __request__, Amazon.Lambda.Core.ILambdaContext __context__)
{
- return authorizerFunction.HttpApiAuthorizeV1(__request__, __context__);
+ var response = authorizerFunction.HttpApiAuthorizeV1(__request__, __context__);
+ return response;
}
private static void SetExecutionEnvironment()
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_HttpApiAuthorize_Generated.g.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_HttpApiAuthorize_Generated.g.cs
index 921c6aa02..96c148b6a 100644
--- a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_HttpApiAuthorize_Generated.g.cs
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_HttpApiAuthorize_Generated.g.cs
@@ -30,12 +30,13 @@ public AuthorizerFunction_HttpApiAuthorize_Generated()
///
/// The generated Lambda function handler for
///
- /// The request object that will be processed by the Lambda function handler.
+ /// The API Gateway authorizer request object that will be processed by the Lambda function handler.
/// The ILambdaContext that provides methods for logging and describing the Lambda environment.
/// Result of the Lambda function execution
public Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerV2SimpleResponse HttpApiAuthorize(Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerV2Request __request__, Amazon.Lambda.Core.ILambdaContext __context__)
{
- return authorizerFunction.HttpApiAuthorize(__request__, __context__);
+ var response = authorizerFunction.HttpApiAuthorize(__request__, __context__);
+ return response;
}
private static void SetExecutionEnvironment()
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_RestApiAuthorize_Generated.g.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_RestApiAuthorize_Generated.g.cs
index 76acd4579..50cf79e80 100644
--- a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_RestApiAuthorize_Generated.g.cs
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_RestApiAuthorize_Generated.g.cs
@@ -30,12 +30,13 @@ public AuthorizerFunction_RestApiAuthorize_Generated()
///
/// The generated Lambda function handler for
///
- /// The request object that will be processed by the Lambda function handler.
+ /// The API Gateway authorizer request object that will be processed by the Lambda function handler.
/// The ILambdaContext that provides methods for logging and describing the Lambda environment.
/// Result of the Lambda function execution
public Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerResponse RestApiAuthorize(Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerRequest __request__, Amazon.Lambda.Core.ILambdaContext __context__)
{
- return authorizerFunction.RestApiAuthorize(__request__, __context__);
+ var response = authorizerFunction.RestApiAuthorize(__request__, __context__);
+ return response;
}
private static void SetExecutionEnvironment()
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_SimpleHttpApiAuthorize_Generated.g.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_SimpleHttpApiAuthorize_Generated.g.cs
new file mode 100644
index 000000000..200cc73d5
--- /dev/null
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_SimpleHttpApiAuthorize_Generated.g.cs
@@ -0,0 +1,79 @@
+//
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+using Amazon.Lambda.Core;
+using Amazon.Lambda.Annotations.APIGateway;
+
+namespace TestCustomAuthorizerApp
+{
+ public class AuthorizerFunction_SimpleHttpApiAuthorize_Generated
+ {
+ private readonly AuthorizerFunction authorizerFunction;
+ private readonly Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer serializer;
+
+ ///
+ /// Default constructor. This constructor is used by Lambda to construct the instance. When invoked in a Lambda environment
+ /// the AWS credentials will come from the IAM role associated with the function and the AWS region will be set to the
+ /// region the Lambda function is executed in.
+ ///
+ public AuthorizerFunction_SimpleHttpApiAuthorize_Generated()
+ {
+ SetExecutionEnvironment();
+ authorizerFunction = new AuthorizerFunction();
+ serializer = new Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer();
+ }
+
+ ///
+ /// The generated Lambda function handler for
+ ///
+ /// The API Gateway authorizer request object that will be processed by the Lambda function handler.
+ /// The ILambdaContext that provides methods for logging and describing the Lambda environment.
+ /// Result of the Lambda function execution
+ public System.IO.Stream SimpleHttpApiAuthorize(Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerV2Request __request__, Amazon.Lambda.Core.ILambdaContext __context__)
+ {
+ var authorization = default(string);
+ if (__request__.Headers?.Any(x => string.Equals(x.Key, "Authorization", StringComparison.OrdinalIgnoreCase)) == true)
+ {
+ try
+ {
+ authorization = (string)Convert.ChangeType(__request__.Headers.First(x => string.Equals(x.Key, "Authorization", StringComparison.OrdinalIgnoreCase)).Value, typeof(string));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($"Failed to extract header 'Authorization': {e.Message}");
+ }
+ }
+
+ var authorizerResult = authorizerFunction.SimpleHttpApiAuthorize(authorization, __context__);
+ var serializationOptions = new AuthorizerResultSerializationOptions
+ {
+ Format = AuthorizerResultSerializationOptions.AuthorizerFormat.HttpApiSimple,
+ MethodArn = __request__.RouteArn
+ };
+ var response = (System.IO.Stream)authorizerResult.Serialize(serializationOptions);
+ return response;
+ }
+
+ private static void SetExecutionEnvironment()
+ {
+ const string envName = "AWS_EXECUTION_ENV";
+
+ var envValue = new StringBuilder();
+
+ // If there is an existing execution environment variable add the annotations package as a suffix.
+ if(!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(envName)))
+ {
+ envValue.Append($"{Environment.GetEnvironmentVariable(envName)}_");
+ }
+
+ envValue.Append("lib/amazon-lambda-annotations#{ANNOTATIONS_ASSEMBLY_VERSION}");
+
+ Environment.SetEnvironmentVariable(envName, envValue.ToString());
+ }
+ }
+}
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_SimpleRestApiAuthorize_Generated.g.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_SimpleRestApiAuthorize_Generated.g.cs
new file mode 100644
index 000000000..17a5ac4da
--- /dev/null
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/AuthorizerFunction_SimpleRestApiAuthorize_Generated.g.cs
@@ -0,0 +1,79 @@
+//
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+using Amazon.Lambda.Core;
+using Amazon.Lambda.Annotations.APIGateway;
+
+namespace TestCustomAuthorizerApp
+{
+ public class AuthorizerFunction_SimpleRestApiAuthorize_Generated
+ {
+ private readonly AuthorizerFunction authorizerFunction;
+ private readonly Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer serializer;
+
+ ///
+ /// Default constructor. This constructor is used by Lambda to construct the instance. When invoked in a Lambda environment
+ /// the AWS credentials will come from the IAM role associated with the function and the AWS region will be set to the
+ /// region the Lambda function is executed in.
+ ///
+ public AuthorizerFunction_SimpleRestApiAuthorize_Generated()
+ {
+ SetExecutionEnvironment();
+ authorizerFunction = new AuthorizerFunction();
+ serializer = new Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer();
+ }
+
+ ///
+ /// The generated Lambda function handler for
+ ///
+ /// The API Gateway authorizer request object that will be processed by the Lambda function handler.
+ /// The ILambdaContext that provides methods for logging and describing the Lambda environment.
+ /// Result of the Lambda function execution
+ public System.IO.Stream SimpleRestApiAuthorize(Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerRequest __request__, Amazon.Lambda.Core.ILambdaContext __context__)
+ {
+ var authorization = default(string);
+ if (!string.IsNullOrEmpty(__request__.AuthorizationToken))
+ {
+ try
+ {
+ authorization = (string)Convert.ChangeType(__request__.AuthorizationToken, typeof(string));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($"Failed to extract authorization token: {e.Message}");
+ }
+ }
+
+ var authorizerResult = authorizerFunction.SimpleRestApiAuthorize(authorization, __context__);
+ var serializationOptions = new AuthorizerResultSerializationOptions
+ {
+ Format = AuthorizerResultSerializationOptions.AuthorizerFormat.RestApi,
+ MethodArn = __request__.MethodArn
+ };
+ var response = (System.IO.Stream)authorizerResult.Serialize(serializationOptions);
+ return response;
+ }
+
+ private static void SetExecutionEnvironment()
+ {
+ const string envName = "AWS_EXECUTION_ENV";
+
+ var envValue = new StringBuilder();
+
+ // If there is an existing execution environment variable add the annotations package as a suffix.
+ if(!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(envName)))
+ {
+ envValue.Append($"{Environment.GetEnvironmentVariable(envName)}_");
+ }
+
+ envValue.Append("lib/amazon-lambda-annotations#{ANNOTATIONS_ASSEMBLY_VERSION}");
+
+ Environment.SetEnvironmentVariable(envName, envValue.ToString());
+ }
+ }
+}
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/IAuthorizerResultExample_SimpleHttpApiAuthorizer_Generated.g.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/IAuthorizerResultExample_SimpleHttpApiAuthorizer_Generated.g.cs
new file mode 100644
index 000000000..9069fdd80
--- /dev/null
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/IAuthorizerResultExample_SimpleHttpApiAuthorizer_Generated.g.cs
@@ -0,0 +1,79 @@
+//
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+using Amazon.Lambda.Core;
+using Amazon.Lambda.Annotations.APIGateway;
+
+namespace TestServerlessApp
+{
+ public class IAuthorizerResultExample_SimpleHttpApiAuthorizer_Generated
+ {
+ private readonly IAuthorizerResultExample iAuthorizerResultExample;
+ private readonly Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer serializer;
+
+ ///
+ /// Default constructor. This constructor is used by Lambda to construct the instance. When invoked in a Lambda environment
+ /// the AWS credentials will come from the IAM role associated with the function and the AWS region will be set to the
+ /// region the Lambda function is executed in.
+ ///
+ public IAuthorizerResultExample_SimpleHttpApiAuthorizer_Generated()
+ {
+ SetExecutionEnvironment();
+ iAuthorizerResultExample = new IAuthorizerResultExample();
+ serializer = new Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer();
+ }
+
+ ///
+ /// The generated Lambda function handler for
+ ///
+ /// The API Gateway authorizer request object that will be processed by the Lambda function handler.
+ /// The ILambdaContext that provides methods for logging and describing the Lambda environment.
+ /// Result of the Lambda function execution
+ public System.IO.Stream SimpleHttpApiAuthorizer(Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerV2Request __request__, Amazon.Lambda.Core.ILambdaContext __context__)
+ {
+ var authorization = default(string);
+ if (__request__.Headers?.Any(x => string.Equals(x.Key, "Authorization", StringComparison.OrdinalIgnoreCase)) == true)
+ {
+ try
+ {
+ authorization = (string)Convert.ChangeType(__request__.Headers.First(x => string.Equals(x.Key, "Authorization", StringComparison.OrdinalIgnoreCase)).Value, typeof(string));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($"Failed to extract header 'Authorization': {e.Message}");
+ }
+ }
+
+ var authorizerResult = iAuthorizerResultExample.SimpleHttpApiAuthorizer(authorization, __context__);
+ var serializationOptions = new AuthorizerResultSerializationOptions
+ {
+ Format = AuthorizerResultSerializationOptions.AuthorizerFormat.HttpApiSimple,
+ MethodArn = __request__.RouteArn
+ };
+ var response = (System.IO.Stream)authorizerResult.Serialize(serializationOptions);
+ return response;
+ }
+
+ private static void SetExecutionEnvironment()
+ {
+ const string envName = "AWS_EXECUTION_ENV";
+
+ var envValue = new StringBuilder();
+
+ // If there is an existing execution environment variable add the annotations package as a suffix.
+ if(!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(envName)))
+ {
+ envValue.Append($"{Environment.GetEnvironmentVariable(envName)}_");
+ }
+
+ envValue.Append("lib/amazon-lambda-annotations#{ANNOTATIONS_ASSEMBLY_VERSION}");
+
+ Environment.SetEnvironmentVariable(envName, envValue.ToString());
+ }
+ }
+}
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/IAuthorizerResultExample_SimpleRestApiAuthorizer_Generated.g.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/IAuthorizerResultExample_SimpleRestApiAuthorizer_Generated.g.cs
new file mode 100644
index 000000000..da9ea10fa
--- /dev/null
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/IAuthorizerResultExample_SimpleRestApiAuthorizer_Generated.g.cs
@@ -0,0 +1,79 @@
+//
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+using Amazon.Lambda.Core;
+using Amazon.Lambda.Annotations.APIGateway;
+
+namespace TestServerlessApp
+{
+ public class IAuthorizerResultExample_SimpleRestApiAuthorizer_Generated
+ {
+ private readonly IAuthorizerResultExample iAuthorizerResultExample;
+ private readonly Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer serializer;
+
+ ///
+ /// Default constructor. This constructor is used by Lambda to construct the instance. When invoked in a Lambda environment
+ /// the AWS credentials will come from the IAM role associated with the function and the AWS region will be set to the
+ /// region the Lambda function is executed in.
+ ///
+ public IAuthorizerResultExample_SimpleRestApiAuthorizer_Generated()
+ {
+ SetExecutionEnvironment();
+ iAuthorizerResultExample = new IAuthorizerResultExample();
+ serializer = new Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer();
+ }
+
+ ///
+ /// The generated Lambda function handler for
+ ///
+ /// The API Gateway authorizer request object that will be processed by the Lambda function handler.
+ /// The ILambdaContext that provides methods for logging and describing the Lambda environment.
+ /// Result of the Lambda function execution
+ public System.IO.Stream SimpleRestApiAuthorizer(Amazon.Lambda.APIGatewayEvents.APIGatewayCustomAuthorizerRequest __request__, Amazon.Lambda.Core.ILambdaContext __context__)
+ {
+ var authorization = default(string);
+ if (!string.IsNullOrEmpty(__request__.AuthorizationToken))
+ {
+ try
+ {
+ authorization = (string)Convert.ChangeType(__request__.AuthorizationToken, typeof(string));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+ __context__.Logger.Log($"Failed to extract authorization token: {e.Message}");
+ }
+ }
+
+ var authorizerResult = iAuthorizerResultExample.SimpleRestApiAuthorizer(authorization, __context__);
+ var serializationOptions = new AuthorizerResultSerializationOptions
+ {
+ Format = AuthorizerResultSerializationOptions.AuthorizerFormat.RestApi,
+ MethodArn = __request__.MethodArn
+ };
+ var response = (System.IO.Stream)authorizerResult.Serialize(serializationOptions);
+ return response;
+ }
+
+ private static void SetExecutionEnvironment()
+ {
+ const string envName = "AWS_EXECUTION_ENV";
+
+ var envValue = new StringBuilder();
+
+ // If there is an existing execution environment variable add the annotations package as a suffix.
+ if(!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(envName)))
+ {
+ envValue.Append($"{Environment.GetEnvironmentVariable(envName)}_");
+ }
+
+ envValue.Append("lib/amazon-lambda-annotations#{ANNOTATIONS_ASSEMBLY_VERSION}");
+
+ Environment.SetEnvironmentVariable(envName, envValue.ToString());
+ }
+ }
+}
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ProtectedFunction_GetSimpleHttpApiUserInfo_Generated.g.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ProtectedFunction_GetSimpleHttpApiUserInfo_Generated.g.cs
new file mode 100644
index 000000000..784c96531
--- /dev/null
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ProtectedFunction_GetSimpleHttpApiUserInfo_Generated.g.cs
@@ -0,0 +1,225 @@
+//
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+using Amazon.Lambda.Core;
+
+namespace TestCustomAuthorizerApp
+{
+ public class ProtectedFunction_GetSimpleHttpApiUserInfo_Generated
+ {
+ private readonly ProtectedFunction protectedFunction;
+ private readonly Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer serializer;
+
+ ///
+ /// Default constructor. This constructor is used by Lambda to construct the instance. When invoked in a Lambda environment
+ /// the AWS credentials will come from the IAM role associated with the function and the AWS region will be set to the
+ /// region the Lambda function is executed in.
+ ///
+ public ProtectedFunction_GetSimpleHttpApiUserInfo_Generated()
+ {
+ SetExecutionEnvironment();
+ protectedFunction = new ProtectedFunction();
+ serializer = new Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer();
+ }
+
+ ///
+ /// The generated Lambda function handler for
+ ///
+ /// The API Gateway request object that will be processed by the Lambda function handler.
+ /// The ILambdaContext that provides methods for logging and describing the Lambda environment.
+ /// Result of the Lambda function execution
+ public Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse GetSimpleHttpApiUserInfo(Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyRequest __request__, Amazon.Lambda.Core.ILambdaContext __context__)
+ {
+ var validationErrors = new List();
+
+ var userId = default(string);
+ if (__request__.RequestContext?.Authorizer?.Lambda == null || __request__.RequestContext?.Authorizer?.Lambda.ContainsKey("userId") == false)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogDebug("Authorizer attribute 'userId' was missing, returning unauthorized.");
+#else
+ __context__.Logger.Log("Authorizer attribute 'userId' was missing, returning unauthorized.");
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ try
+ {
+ var __authValue_userId__ = __request__.RequestContext.Authorizer.Lambda["userId"];
+ userId = (string)Convert.ChangeType(__authValue_userId__?.ToString(), typeof(string));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogError(e, "Failed to convert authorizer attribute 'userId', returning unauthorized.");
+#else
+ __context__.Logger.Log("Failed to convert authorizer attribute 'userId', returning unauthorized. Exception: " + e.ToString());
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ var email = default(string);
+ if (__request__.RequestContext?.Authorizer?.Lambda == null || __request__.RequestContext?.Authorizer?.Lambda.ContainsKey("email") == false)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogDebug("Authorizer attribute 'email' was missing, returning unauthorized.");
+#else
+ __context__.Logger.Log("Authorizer attribute 'email' was missing, returning unauthorized.");
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ try
+ {
+ var __authValue_email__ = __request__.RequestContext.Authorizer.Lambda["email"];
+ email = (string)Convert.ChangeType(__authValue_email__?.ToString(), typeof(string));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogError(e, "Failed to convert authorizer attribute 'email', returning unauthorized.");
+#else
+ __context__.Logger.Log("Failed to convert authorizer attribute 'email', returning unauthorized. Exception: " + e.ToString());
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ var tenantId = default(string);
+ if (__request__.RequestContext?.Authorizer?.Lambda == null || __request__.RequestContext?.Authorizer?.Lambda.ContainsKey("tenantId") == false)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogDebug("Authorizer attribute 'tenantId' was missing, returning unauthorized.");
+#else
+ __context__.Logger.Log("Authorizer attribute 'tenantId' was missing, returning unauthorized.");
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ try
+ {
+ var __authValue_tenantId__ = __request__.RequestContext.Authorizer.Lambda["tenantId"];
+ tenantId = (string)Convert.ChangeType(__authValue_tenantId__?.ToString(), typeof(string));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogError(e, "Failed to convert authorizer attribute 'tenantId', returning unauthorized.");
+#else
+ __context__.Logger.Log("Failed to convert authorizer attribute 'tenantId', returning unauthorized. Exception: " + e.ToString());
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ // return 400 Bad Request if there exists a validation error
+ if (validationErrors.Any())
+ {
+ var errorResult = new Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse
+ {
+ Body = @$"{{""message"": ""{validationErrors.Count} validation error(s) detected: {string.Join(",", validationErrors)}""}}",
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "ValidationException"}
+ },
+ StatusCode = 400
+ };
+ return errorResult;
+ }
+
+ var response = protectedFunction.GetSimpleHttpApiUserInfo(userId, email, tenantId, __context__);
+ var memoryStream = new MemoryStream();
+ serializer.Serialize(response, memoryStream);
+ memoryStream.Position = 0;
+
+ // convert stream to string
+ StreamReader reader = new StreamReader( memoryStream );
+ var body = reader.ReadToEnd();
+
+ return new Amazon.Lambda.APIGatewayEvents.APIGatewayHttpApiV2ProxyResponse
+ {
+ Body = body,
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"}
+ },
+ StatusCode = 200
+ };
+ }
+
+ private static void SetExecutionEnvironment()
+ {
+ const string envName = "AWS_EXECUTION_ENV";
+
+ var envValue = new StringBuilder();
+
+ // If there is an existing execution environment variable add the annotations package as a suffix.
+ if(!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(envName)))
+ {
+ envValue.Append($"{Environment.GetEnvironmentVariable(envName)}_");
+ }
+
+ envValue.Append("lib/amazon-lambda-annotations#{ANNOTATIONS_ASSEMBLY_VERSION}");
+
+ Environment.SetEnvironmentVariable(envName, envValue.ToString());
+ }
+ }
+}
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ProtectedFunction_GetSimpleRestApiUserInfo_Generated.g.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ProtectedFunction_GetSimpleRestApiUserInfo_Generated.g.cs
new file mode 100644
index 000000000..f81b75d86
--- /dev/null
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ProtectedFunction_GetSimpleRestApiUserInfo_Generated.g.cs
@@ -0,0 +1,225 @@
+//
+
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using System.IO;
+using Amazon.Lambda.Core;
+
+namespace TestCustomAuthorizerApp
+{
+ public class ProtectedFunction_GetSimpleRestApiUserInfo_Generated
+ {
+ private readonly ProtectedFunction protectedFunction;
+ private readonly Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer serializer;
+
+ ///
+ /// Default constructor. This constructor is used by Lambda to construct the instance. When invoked in a Lambda environment
+ /// the AWS credentials will come from the IAM role associated with the function and the AWS region will be set to the
+ /// region the Lambda function is executed in.
+ ///
+ public ProtectedFunction_GetSimpleRestApiUserInfo_Generated()
+ {
+ SetExecutionEnvironment();
+ protectedFunction = new ProtectedFunction();
+ serializer = new Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer();
+ }
+
+ ///
+ /// The generated Lambda function handler for
+ ///
+ /// The API Gateway request object that will be processed by the Lambda function handler.
+ /// The ILambdaContext that provides methods for logging and describing the Lambda environment.
+ /// Result of the Lambda function execution
+ public Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse GetSimpleRestApiUserInfo(Amazon.Lambda.APIGatewayEvents.APIGatewayProxyRequest __request__, Amazon.Lambda.Core.ILambdaContext __context__)
+ {
+ var validationErrors = new List();
+
+ var userId = default(string);
+ if (__request__.RequestContext?.Authorizer == null || __request__.RequestContext?.Authorizer.ContainsKey("userId") == false)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogDebug("Authorizer attribute 'userId' was missing, returning unauthorized.");
+#else
+ __context__.Logger.Log("Authorizer attribute 'userId' was missing, returning unauthorized.");
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ try
+ {
+ var __authValue_userId__ = __request__.RequestContext.Authorizer["userId"];
+ userId = (string)Convert.ChangeType(__authValue_userId__?.ToString(), typeof(string));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogError(e, "Failed to convert authorizer attribute 'userId', returning unauthorized.");
+#else
+ __context__.Logger.Log("Failed to convert authorizer attribute 'userId', returning unauthorized. Exception: " + e.ToString());
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ var email = default(string);
+ if (__request__.RequestContext?.Authorizer == null || __request__.RequestContext?.Authorizer.ContainsKey("email") == false)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogDebug("Authorizer attribute 'email' was missing, returning unauthorized.");
+#else
+ __context__.Logger.Log("Authorizer attribute 'email' was missing, returning unauthorized.");
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ try
+ {
+ var __authValue_email__ = __request__.RequestContext.Authorizer["email"];
+ email = (string)Convert.ChangeType(__authValue_email__?.ToString(), typeof(string));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogError(e, "Failed to convert authorizer attribute 'email', returning unauthorized.");
+#else
+ __context__.Logger.Log("Failed to convert authorizer attribute 'email', returning unauthorized. Exception: " + e.ToString());
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ var tenantId = default(string);
+ if (__request__.RequestContext?.Authorizer == null || __request__.RequestContext?.Authorizer.ContainsKey("tenantId") == false)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogDebug("Authorizer attribute 'tenantId' was missing, returning unauthorized.");
+#else
+ __context__.Logger.Log("Authorizer attribute 'tenantId' was missing, returning unauthorized.");
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ try
+ {
+ var __authValue_tenantId__ = __request__.RequestContext.Authorizer["tenantId"];
+ tenantId = (string)Convert.ChangeType(__authValue_tenantId__?.ToString(), typeof(string));
+ }
+ catch (Exception e) when (e is InvalidCastException || e is FormatException || e is OverflowException || e is ArgumentException)
+ {
+#if NET6_0_OR_GREATER
+ __context__.Logger.LogError(e, "Failed to convert authorizer attribute 'tenantId', returning unauthorized.");
+#else
+ __context__.Logger.Log("Failed to convert authorizer attribute 'tenantId', returning unauthorized. Exception: " + e.ToString());
+#endif
+ var __unauthorized__ = new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse
+ {
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "AccessDeniedException"}
+ },
+ StatusCode = 401
+ };
+ return __unauthorized__;
+ }
+
+ // return 400 Bad Request if there exists a validation error
+ if (validationErrors.Any())
+ {
+ var errorResult = new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse
+ {
+ Body = @$"{{""message"": ""{validationErrors.Count} validation error(s) detected: {string.Join(",", validationErrors)}""}}",
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"},
+ {"x-amzn-ErrorType", "ValidationException"}
+ },
+ StatusCode = 400
+ };
+ return errorResult;
+ }
+
+ var response = protectedFunction.GetSimpleRestApiUserInfo(userId, email, tenantId, __context__);
+ var memoryStream = new MemoryStream();
+ serializer.Serialize(response, memoryStream);
+ memoryStream.Position = 0;
+
+ // convert stream to string
+ StreamReader reader = new StreamReader( memoryStream );
+ var body = reader.ReadToEnd();
+
+ return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse
+ {
+ Body = body,
+ Headers = new Dictionary
+ {
+ {"Content-Type", "application/json"}
+ },
+ StatusCode = 200
+ };
+ }
+
+ private static void SetExecutionEnvironment()
+ {
+ const string envName = "AWS_EXECUTION_ENV";
+
+ var envValue = new StringBuilder();
+
+ // If there is an existing execution environment variable add the annotations package as a suffix.
+ if(!string.IsNullOrEmpty(Environment.GetEnvironmentVariable(envName)))
+ {
+ envValue.Append($"{Environment.GetEnvironmentVariable(envName)}_");
+ }
+
+ envValue.Append("lib/amazon-lambda-annotations#{ANNOTATIONS_ASSEMBLY_VERSION}");
+
+ Environment.SetEnvironmentVariable(envName, envValue.ToString());
+ }
+ }
+}
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/authorizerIAuthorizerResult.template b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/authorizerIAuthorizerResult.template
new file mode 100644
index 000000000..9ab460b89
--- /dev/null
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/authorizerIAuthorizerResult.template
@@ -0,0 +1,102 @@
+{
+ "AWSTemplateFormatVersion": "2010-09-09",
+ "Transform": "AWS::Serverless-2016-10-31",
+ "Description": "This template is partially managed by Amazon.Lambda.Annotations (v{ANNOTATIONS_ASSEMBLY_VERSION}).",
+ "Resources": {
+ "AnnotationsHttpApi": {
+ "Type": "AWS::Serverless::HttpApi",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Auth": {
+ "Authorizers": {
+ "SimpleHttpApiAuthorizer": {
+ "FunctionArn": {
+ "Fn::GetAtt": [
+ "SimpleHttpApiAuth",
+ "Arn"
+ ]
+ },
+ "AuthorizerPayloadFormatVersion": "2.0",
+ "EnableSimpleResponses": true,
+ "Identity": {
+ "Headers": [
+ "Authorization"
+ ]
+ },
+ "EnableFunctionDefaultPermissions": true,
+ "AuthorizerResultTtlInSeconds": 0
+ }
+ }
+ }
+ }
+ },
+ "AnnotationsRestApi": {
+ "Type": "AWS::Serverless::Api",
+ "Properties": {
+ "StageName": "Prod",
+ "Auth": {
+ "Authorizers": {
+ "SimpleRestApiAuthorizer": {
+ "FunctionArn": {
+ "Fn::GetAtt": [
+ "SimpleRestApiAuth",
+ "Arn"
+ ]
+ },
+ "Identity": {
+ "Header": "Authorization"
+ },
+ "FunctionPayloadType": "TOKEN",
+ "AuthorizerResultTtlInSeconds": 0
+ }
+ }
+ }
+ },
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ }
+ },
+ "SimpleHttpApiAuth": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Image",
+ "ImageUri": ".",
+ "ImageConfig": {
+ "Command": [
+ "TestProject::TestServerlessApp.IAuthorizerResultExample_SimpleHttpApiAuthorizer_Generated::SimpleHttpApiAuthorizer"
+ ]
+ }
+ }
+ },
+ "SimpleRestApiAuth": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Image",
+ "ImageUri": ".",
+ "ImageConfig": {
+ "Command": [
+ "TestProject::TestServerlessApp.IAuthorizerResultExample_SimpleRestApiAuthorizer_Generated::SimpleRestApiAuthorizer"
+ ]
+ }
+ }
+ }
+ }
+}
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/customAuthorizerApp.template b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/customAuthorizerApp.template
index e2a7480d2..d04e193ac 100644
--- a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/customAuthorizerApp.template
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/Snapshots/ServerlessTemplates/customAuthorizerApp.template
@@ -44,6 +44,23 @@
},
"EnableFunctionDefaultPermissions": true,
"AuthorizerResultTtlInSeconds": 0
+ },
+ "SimpleHttpApiAuthorize": {
+ "FunctionArn": {
+ "Fn::GetAtt": [
+ "SimpleAuthorizer",
+ "Arn"
+ ]
+ },
+ "AuthorizerPayloadFormatVersion": "2.0",
+ "EnableSimpleResponses": true,
+ "Identity": {
+ "Headers": [
+ "Authorization"
+ ]
+ },
+ "EnableFunctionDefaultPermissions": true,
+ "AuthorizerResultTtlInSeconds": 0
}
}
}
@@ -67,6 +84,19 @@
},
"FunctionPayloadType": "TOKEN",
"AuthorizerResultTtlInSeconds": 0
+ },
+ "SimpleRestApiAuthorize": {
+ "FunctionArn": {
+ "Fn::GetAtt": [
+ "SimpleRestAuthorizer",
+ "Arn"
+ ]
+ },
+ "Identity": {
+ "Header": "Authorization"
+ },
+ "FunctionPayloadType": "TOKEN",
+ "AuthorizerResultTtlInSeconds": 0
}
}
}
@@ -126,6 +156,40 @@
"Handler": "TestProject::TestCustomAuthorizerApp.AuthorizerFunction_RestApiAuthorize_Generated::RestApiAuthorize"
}
},
+ "SimpleAuthorizer": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestProject::TestCustomAuthorizerApp.AuthorizerFunction_SimpleHttpApiAuthorize_Generated::SimpleHttpApiAuthorize"
+ }
+ },
+ "SimpleRestAuthorizer": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestProject::TestCustomAuthorizerApp.AuthorizerFunction_SimpleRestApiAuthorize_Generated::SimpleRestApiAuthorize"
+ }
+ },
"ProtectedEndpoint": {
"Type": "AWS::Serverless::Function",
"Metadata": {
@@ -382,6 +446,92 @@
}
}
},
+ "SimpleHttpApiUserInfo": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations",
+ "SyncedEvents": [
+ "RootGet"
+ ],
+ "SyncedEventProperties": {
+ "RootGet": [
+ "Path",
+ "Method",
+ "Auth.Authorizer",
+ "ApiId.Ref"
+ ]
+ }
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestProject::TestCustomAuthorizerApp.ProtectedFunction_GetSimpleHttpApiUserInfo_Generated::GetSimpleHttpApiUserInfo",
+ "Events": {
+ "RootGet": {
+ "Type": "HttpApi",
+ "Properties": {
+ "Path": "/api/simple-httpapi-user-info",
+ "Method": "GET",
+ "Auth": {
+ "Authorizer": "SimpleHttpApiAuthorize"
+ },
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
+ }
+ }
+ }
+ }
+ },
+ "SimpleRestApiUserInfo": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations",
+ "SyncedEvents": [
+ "RootGet"
+ ],
+ "SyncedEventProperties": {
+ "RootGet": [
+ "Path",
+ "Method",
+ "Auth.Authorizer",
+ "RestApiId.Ref"
+ ]
+ }
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestProject::TestCustomAuthorizerApp.ProtectedFunction_GetSimpleRestApiUserInfo_Generated::GetSimpleRestApiUserInfo",
+ "Events": {
+ "RootGet": {
+ "Type": "Api",
+ "Properties": {
+ "Path": "/api/simple-restapi-user-info",
+ "Method": "GET",
+ "Auth": {
+ "Authorizer": "SimpleRestApiAuthorize"
+ },
+ "RestApiId": {
+ "Ref": "AnnotationsRestApi"
+ }
+ }
+ }
+ }
+ }
+ },
"NonStringUserInfo": {
"Type": "AWS::Serverless::Function",
"Metadata": {
@@ -426,4 +576,4 @@
}
}
}
-}
+}
\ No newline at end of file
diff --git a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs
index 1995743a2..63e746059 100644
--- a/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs
+++ b/Libraries/test/Amazon.Lambda.Annotations.SourceGenerators.Tests/SourceGeneratorTests.cs
@@ -1656,12 +1656,16 @@ public async Task CustomAuthorizerAppAuthorizerDefinitionsTest()
var expectedHttpApiAuthorizeGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "AuthorizerFunction_HttpApiAuthorize_Generated.g.cs"));
var expectedHttpApiAuthorizeV1Generated = await ReadSnapshotContent(Path.Combine("Snapshots", "AuthorizerFunction_HttpApiAuthorizeV1_Generated.g.cs"));
var expectedRestApiAuthorizeGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "AuthorizerFunction_RestApiAuthorize_Generated.g.cs"));
+ var expectedSimpleHttpApiAuthorizeGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "AuthorizerFunction_SimpleHttpApiAuthorize_Generated.g.cs"));
+ var expectedSimpleRestApiAuthorizeGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "AuthorizerFunction_SimpleRestApiAuthorize_Generated.g.cs"));
var expectedGetProtectedDataGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "ProtectedFunction_GetProtectedData_Generated.g.cs"));
var expectedGetUserInfoGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "ProtectedFunction_GetUserInfo_Generated.g.cs"));
var expectedHealthCheckGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "ProtectedFunction_HealthCheck_Generated.g.cs"));
var expectedGetRestUserInfoGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "ProtectedFunction_GetRestUserInfo_Generated.g.cs"));
var expectedGetHttpApiV1UserInfoGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "ProtectedFunction_GetHttpApiV1UserInfo_Generated.g.cs"));
var expectedGetIHttpResultGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "ProtectedFunction_GetIHttpResult_Generated.g.cs"));
+ var expectedGetSimpleHttpApiUserInfoGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "ProtectedFunction_GetSimpleHttpApiUserInfo_Generated.g.cs"));
+ var expectedGetSimpleRestApiUserInfoGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "ProtectedFunction_GetSimpleRestApiUserInfo_Generated.g.cs"));
var expectedGetNonStringUserInfoGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "ProtectedFunction_GetNonStringUserInfo_Generated.g.cs"));
await new VerifyCS.Test
@@ -1698,6 +1702,16 @@ public async Task CustomAuthorizerAppAuthorizerDefinitionsTest()
"AuthorizerFunction_RestApiAuthorize_Generated.g.cs",
SourceText.From(expectedRestApiAuthorizeGenerated, Encoding.UTF8, SourceHashAlgorithm.Sha256)
),
+ (
+ typeof(SourceGenerator.Generator),
+ "AuthorizerFunction_SimpleHttpApiAuthorize_Generated.g.cs",
+ SourceText.From(expectedSimpleHttpApiAuthorizeGenerated, Encoding.UTF8, SourceHashAlgorithm.Sha256)
+ ),
+ (
+ typeof(SourceGenerator.Generator),
+ "AuthorizerFunction_SimpleRestApiAuthorize_Generated.g.cs",
+ SourceText.From(expectedSimpleRestApiAuthorizeGenerated, Encoding.UTF8, SourceHashAlgorithm.Sha256)
+ ),
(
typeof(SourceGenerator.Generator),
"ProtectedFunction_GetProtectedData_Generated.g.cs",
@@ -1728,6 +1742,16 @@ public async Task CustomAuthorizerAppAuthorizerDefinitionsTest()
"ProtectedFunction_GetIHttpResult_Generated.g.cs",
SourceText.From(expectedGetIHttpResultGenerated, Encoding.UTF8, SourceHashAlgorithm.Sha256)
),
+ (
+ typeof(SourceGenerator.Generator),
+ "ProtectedFunction_GetSimpleHttpApiUserInfo_Generated.g.cs",
+ SourceText.From(expectedGetSimpleHttpApiUserInfoGenerated, Encoding.UTF8, SourceHashAlgorithm.Sha256)
+ ),
+ (
+ typeof(SourceGenerator.Generator),
+ "ProtectedFunction_GetSimpleRestApiUserInfo_Generated.g.cs",
+ SourceText.From(expectedGetSimpleRestApiUserInfoGenerated, Encoding.UTF8, SourceHashAlgorithm.Sha256)
+ ),
(
typeof(SourceGenerator.Generator),
"ProtectedFunction_GetNonStringUserInfo_Generated.g.cs",
@@ -1739,13 +1763,17 @@ public async Task CustomAuthorizerAppAuthorizerDefinitionsTest()
new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("AuthorizerFunction_HttpApiAuthorizeV1_Generated.g.cs", expectedHttpApiAuthorizeV1Generated),
new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("AuthorizerFunction_HttpApiAuthorize_Generated.g.cs", expectedHttpApiAuthorizeGenerated),
new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("AuthorizerFunction_RestApiAuthorize_Generated.g.cs", expectedRestApiAuthorizeGenerated),
- new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetProtectedData_Generated.g.cs", expectedGetProtectedDataGenerated),
- new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetUserInfo_Generated.g.cs", expectedGetUserInfoGenerated),
- new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_HealthCheck_Generated.g.cs", expectedHealthCheckGenerated),
- new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetRestUserInfo_Generated.g.cs", expectedGetRestUserInfoGenerated),
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("AuthorizerFunction_SimpleHttpApiAuthorize_Generated.g.cs", expectedSimpleHttpApiAuthorizeGenerated),
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("AuthorizerFunction_SimpleRestApiAuthorize_Generated.g.cs", expectedSimpleRestApiAuthorizeGenerated),
new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetHttpApiV1UserInfo_Generated.g.cs", expectedGetHttpApiV1UserInfoGenerated),
new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetIHttpResult_Generated.g.cs", expectedGetIHttpResultGenerated),
new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetNonStringUserInfo_Generated.g.cs", expectedGetNonStringUserInfoGenerated),
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetProtectedData_Generated.g.cs", expectedGetProtectedDataGenerated),
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetRestUserInfo_Generated.g.cs", expectedGetRestUserInfoGenerated),
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetSimpleHttpApiUserInfo_Generated.g.cs", expectedGetSimpleHttpApiUserInfoGenerated),
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetSimpleRestApiUserInfo_Generated.g.cs", expectedGetSimpleRestApiUserInfoGenerated),
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_GetUserInfo_Generated.g.cs", expectedGetUserInfoGenerated),
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("ProtectedFunction_HealthCheck_Generated.g.cs", expectedHealthCheckGenerated),
new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments($"TestCustomAuthorizerApp{Path.DirectorySeparatorChar}serverless.template", expectedTemplateContent),
},
ReferenceAssemblies = ReferenceAssemblies.Net.Net60
@@ -1786,6 +1814,60 @@ public async Task VerifyMissingLambdaFunctionWithAuthorizerAttribute()
await test.RunAsync();
}
+ ///
+ /// Tests the IAuthorizerResult pattern for authorizer functions.
+ /// This validates that [HttpApiAuthorizer] + IAuthorizerResult return type + [FromHeader] generates
+ /// correct handler code that extracts headers from the authorizer request and serializes IAuthorizerResult.
+ ///
+ [Fact]
+ public async Task IAuthorizerResultHttpApiTest()
+ {
+ var expectedTemplateContent = await ReadSnapshotContent(Path.Combine("Snapshots", "ServerlessTemplates", "authorizerIAuthorizerResult.template"));
+ var expectedHttpApiGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "IAuthorizerResultExample_SimpleHttpApiAuthorizer_Generated.g.cs"));
+ var expectedRestApiGenerated = await ReadSnapshotContent(Path.Combine("Snapshots", "IAuthorizerResultExample_SimpleRestApiAuthorizer_Generated.g.cs"));
+
+ await new VerifyCS.Test
+ {
+ TestState =
+ {
+ Sources =
+ {
+ (Path.Combine("TestServerlessApp", "IAuthorizerResultExample.cs"), File.ReadAllText(Path.Combine("TestServerlessApp", "IAuthorizerResultExample.cs"))),
+ (Path.Combine("Amazon.Lambda.Annotations", "LambdaFunctionAttribute.cs"), File.ReadAllText(Path.Combine("Amazon.Lambda.Annotations", "LambdaFunctionAttribute.cs"))),
+ (Path.Combine("Amazon.Lambda.Annotations", "LambdaStartupAttribute.cs"), File.ReadAllText(Path.Combine("Amazon.Lambda.Annotations", "LambdaStartupAttribute.cs"))),
+ (Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "HttpApiAuthorizerAttribute.cs"), File.ReadAllText(Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "HttpApiAuthorizerAttribute.cs"))),
+ (Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "AuthorizerPayloadFormatVersion.cs"), File.ReadAllText(Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "AuthorizerPayloadFormatVersion.cs"))),
+ (Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "RestApiAuthorizerAttribute.cs"), File.ReadAllText(Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "RestApiAuthorizerAttribute.cs"))),
+ (Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "FromHeaderAttribute.cs"), File.ReadAllText(Path.Combine("Amazon.Lambda.Annotations", "APIGateway", "FromHeaderAttribute.cs"))),
+ (Path.Combine("TestServerlessApp", "AssemblyAttributes.cs"), File.ReadAllText(Path.Combine("TestServerlessApp", "AssemblyAttributes.cs"))),
+ },
+ GeneratedSources =
+ {
+ (
+ typeof(SourceGenerator.Generator),
+ "IAuthorizerResultExample_SimpleHttpApiAuthorizer_Generated.g.cs",
+ SourceText.From(expectedHttpApiGenerated, Encoding.UTF8, SourceHashAlgorithm.Sha256)
+ ),
+ (
+ typeof(SourceGenerator.Generator),
+ "IAuthorizerResultExample_SimpleRestApiAuthorizer_Generated.g.cs",
+ SourceText.From(expectedRestApiGenerated, Encoding.UTF8, SourceHashAlgorithm.Sha256)
+ )
+ },
+ ExpectedDiagnostics =
+ {
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("IAuthorizerResultExample_SimpleHttpApiAuthorizer_Generated.g.cs", expectedHttpApiGenerated),
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments("IAuthorizerResultExample_SimpleRestApiAuthorizer_Generated.g.cs", expectedRestApiGenerated),
+ new DiagnosticResult("AWSLambda0103", DiagnosticSeverity.Info).WithArguments($"TestServerlessApp{Path.DirectorySeparatorChar}serverless.template", expectedTemplateContent),
+ },
+ ReferenceAssemblies = ReferenceAssemblies.Net.Net60
+ }
+ }.RunAsync();
+
+ var actualTemplateContent = File.ReadAllText(Path.Combine("TestServerlessApp", "serverless.template"));
+ Assert.Equal(expectedTemplateContent, actualTemplateContent);
+ }
+
public void Dispose()
{
File.Delete(Path.Combine("TestServerlessApp", "serverless.template"));
diff --git a/Libraries/test/TestCustomAuthorizerApp.IntegrationTests/IntegrationTestContextFixture.cs b/Libraries/test/TestCustomAuthorizerApp.IntegrationTests/IntegrationTestContextFixture.cs
index da9169201..f6a9138f4 100644
--- a/Libraries/test/TestCustomAuthorizerApp.IntegrationTests/IntegrationTestContextFixture.cs
+++ b/Libraries/test/TestCustomAuthorizerApp.IntegrationTests/IntegrationTestContextFixture.cs
@@ -82,9 +82,11 @@ public async Task InitializeAsync()
Assert.True(await _s3Helper.BucketExistsAsync(_bucketName), $"S3 bucket {_bucketName} should exist");
- // There are 10 Lambda functions in TestCustomAuthorizerApp:
- // CustomAuthorizer, CustomAuthorizerV1, RestApiAuthorizer, ProtectedEndpoint, GetUserInfo, HealthCheck, RestUserInfo, HttpApiV1UserInfo, IHttpResultUserInfo, NonStringUserInfo
- Assert.Equal(10, LambdaFunctions.Count);
+ // There are 14 Lambda functions in TestCustomAuthorizerApp:
+ // CustomAuthorizer, CustomAuthorizerV1, RestApiAuthorizer, SimpleAuthorizer, SimpleRestAuthorizer,
+ // ProtectedEndpoint, GetUserInfo, HealthCheck, RestUserInfo, HttpApiV1UserInfo, IHttpResultUserInfo, NonStringUserInfo,
+ // SimpleHttpApiUserInfo, SimpleRestApiUserInfo
+ Assert.Equal(14, LambdaFunctions.Count);
await LambdaHelper.WaitTillNotPending(LambdaFunctions.Where(x => x.Name != null).Select(x => x.Name!).ToList());
diff --git a/Libraries/test/TestCustomAuthorizerApp.IntegrationTests/SimpleHttpApiAuthorizerTests.cs b/Libraries/test/TestCustomAuthorizerApp.IntegrationTests/SimpleHttpApiAuthorizerTests.cs
new file mode 100644
index 000000000..468caa3b4
--- /dev/null
+++ b/Libraries/test/TestCustomAuthorizerApp.IntegrationTests/SimpleHttpApiAuthorizerTests.cs
@@ -0,0 +1,94 @@
+using System.Net;
+using System.Net.Http.Headers;
+using Newtonsoft.Json.Linq;
+using Xunit;
+
+namespace TestCustomAuthorizerApp.IntegrationTests;
+
+///
+/// Tests for endpoints protected by the IAuthorizerResult-based HTTP API authorizer.
+/// These verify the end-to-end flow: IAuthorizerResult.Serialize() produces the correct
+/// simple response JSON → API Gateway accepts it → protected endpoint receives context values.
+///
+/// The authorizer under test is
+/// which returns IAuthorizerResult (AuthorizerResults.Allow()/Deny()) instead of raw API Gateway types.
+///
+[Collection("Integration Tests")]
+public class SimpleHttpApiAuthorizerTests
+{
+ private readonly IntegrationTestContextFixture _fixture;
+
+ public SimpleHttpApiAuthorizerTests(IntegrationTestContextFixture fixture)
+ {
+ _fixture = fixture;
+ }
+
+ ///
+ /// Tests that an IAuthorizerResult-based HTTP API authorizer correctly allows requests
+ /// and passes context values to the protected endpoint.
+ ///
+ /// Flow: Request with valid token → IAuthorizerResult authorizer calls AuthorizerResults.Allow().WithContext(...)
+ /// → generated handler serializes to simple response format → API Gateway accepts →
+ /// protected endpoint extracts context via [FromCustomAuthorizer] → returns values
+ ///
+ [Fact]
+ public async Task SimpleHttpApiUserInfo_WithValidAuth_ReturnsAuthorizerContext()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Get, $"{_fixture.HttpApiUrl}/api/simple-httpapi-user-info");
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token");
+
+ // Act
+ var response = await _fixture.HttpClient.SendAsync(request);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+ var json = JObject.Parse(content);
+
+ // Verify FromCustomAuthorizer extracted the values set by AuthorizerResults.Allow().WithContext(...)
+ Assert.Equal("user-12345", json["UserId"]?.ToString());
+ Assert.Equal("test@example.com", json["Email"]?.ToString());
+ Assert.Equal("42", json["TenantId"]?.ToString());
+ Assert.Equal("Simple HTTP API (IAuthorizerResult)", json["ApiType"]?.ToString());
+ }
+
+ ///
+ /// Tests that an IAuthorizerResult-based HTTP API authorizer correctly denies requests
+ /// when AuthorizerResults.Deny() is returned.
+ ///
+ /// Flow: Request with invalid token → IAuthorizerResult authorizer calls AuthorizerResults.Deny()
+ /// → generated handler serializes { isAuthorized: false } → API Gateway returns 403
+ ///
+ [Fact]
+ public async Task SimpleHttpApiUserInfo_WithInvalidAuth_Returns403()
+ {
+ // Arrange - use an invalid token that the authorizer will deny
+ var request = new HttpRequestMessage(HttpMethod.Get, $"{_fixture.HttpApiUrl}/api/simple-httpapi-user-info");
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "invalid-token");
+
+ // Act
+ var response = await _fixture.HttpClient.SendAsync(request);
+
+ // Assert - API Gateway returns 403 when the authorizer denies the request
+ Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
+ }
+
+ ///
+ /// Tests that the IAuthorizerResult-based authorizer denies requests with no authorization header.
+ ///
+ [Fact]
+ public async Task SimpleHttpApiUserInfo_WithNoAuth_Returns403()
+ {
+ // Arrange - no authorization header
+ var request = new HttpRequestMessage(HttpMethod.Get, $"{_fixture.HttpApiUrl}/api/simple-httpapi-user-info");
+
+ // Act
+ var response = await _fixture.HttpClient.SendAsync(request);
+
+ // Assert - API Gateway returns 401 or 403 when no auth is provided
+ Assert.True(
+ response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden,
+ $"Expected 401 or 403, but got {(int)response.StatusCode} {response.StatusCode}");
+ }
+}
diff --git a/Libraries/test/TestCustomAuthorizerApp.IntegrationTests/SimpleRestApiAuthorizerTests.cs b/Libraries/test/TestCustomAuthorizerApp.IntegrationTests/SimpleRestApiAuthorizerTests.cs
new file mode 100644
index 000000000..3d64ba6c2
--- /dev/null
+++ b/Libraries/test/TestCustomAuthorizerApp.IntegrationTests/SimpleRestApiAuthorizerTests.cs
@@ -0,0 +1,97 @@
+using System.Net;
+using System.Net.Http.Headers;
+using Newtonsoft.Json.Linq;
+using Xunit;
+
+namespace TestCustomAuthorizerApp.IntegrationTests;
+
+///
+/// Tests for endpoints protected by the IAuthorizerResult-based REST API authorizer.
+/// These verify the end-to-end flow: IAuthorizerResult.Serialize() produces the correct
+/// IAM policy JSON → API Gateway accepts it → protected endpoint receives context values.
+///
+/// The authorizer under test is
+/// which returns IAuthorizerResult (AuthorizerResults.Allow()/Deny()) instead of raw API Gateway types.
+/// The generated handler serializes this to an IAM policy document with the correct MethodArn.
+///
+[Collection("Integration Tests")]
+public class SimpleRestApiAuthorizerTests
+{
+ private readonly IntegrationTestContextFixture _fixture;
+
+ public SimpleRestApiAuthorizerTests(IntegrationTestContextFixture fixture)
+ {
+ _fixture = fixture;
+ }
+
+ ///
+ /// Tests that an IAuthorizerResult-based REST API authorizer correctly allows requests
+ /// and passes context values to the protected endpoint.
+ ///
+ /// Flow: Request with valid token → IAuthorizerResult authorizer calls
+ /// AuthorizerResults.Allow().WithPrincipalId(...).WithContext(...)
+ /// → generated handler serializes to IAM policy with Allow effect → API Gateway accepts →
+ /// protected endpoint extracts context via [FromCustomAuthorizer] → returns values
+ ///
+ [Fact]
+ public async Task SimpleRestApiUserInfo_WithValidAuth_ReturnsAuthorizerContext()
+ {
+ // Arrange
+ var request = new HttpRequestMessage(HttpMethod.Get, $"{_fixture.RestApiUrl}/api/simple-restapi-user-info");
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token");
+
+ // Act
+ var response = await _fixture.HttpClient.SendAsync(request);
+
+ // Assert
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ var content = await response.Content.ReadAsStringAsync();
+ var json = JObject.Parse(content);
+
+ // Verify FromCustomAuthorizer extracted the values set by AuthorizerResults.Allow().WithContext(...)
+ Assert.Equal("user-12345", json["UserId"]?.ToString());
+ Assert.Equal("test@example.com", json["Email"]?.ToString());
+ Assert.Equal("42", json["TenantId"]?.ToString());
+ Assert.Equal("Simple REST API (IAuthorizerResult)", json["ApiType"]?.ToString());
+ }
+
+ ///
+ /// Tests that an IAuthorizerResult-based REST API authorizer correctly denies requests
+ /// when AuthorizerResults.Deny() is returned.
+ ///
+ /// Flow: Request with invalid token → IAuthorizerResult authorizer calls AuthorizerResults.Deny()
+ /// → generated handler serializes IAM policy with Deny effect → API Gateway returns 403
+ ///
+ [Fact]
+ public async Task SimpleRestApiUserInfo_WithInvalidAuth_Returns403()
+ {
+ // Arrange - use an invalid token that the authorizer will deny
+ var request = new HttpRequestMessage(HttpMethod.Get, $"{_fixture.RestApiUrl}/api/simple-restapi-user-info");
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "invalid-token");
+
+ // Act
+ var response = await _fixture.HttpClient.SendAsync(request);
+
+ // Assert - API Gateway returns 403 when the authorizer denies via IAM policy
+ Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
+ }
+
+ ///
+ /// Tests that the IAuthorizerResult-based REST API authorizer denies requests with no authorization header.
+ /// REST API token authorizers return 401 when the identity source (Authorization header) is missing.
+ ///
+ [Fact]
+ public async Task SimpleRestApiUserInfo_WithNoAuth_ReturnsUnauthorized()
+ {
+ // Arrange - no authorization header
+ var request = new HttpRequestMessage(HttpMethod.Get, $"{_fixture.RestApiUrl}/api/simple-restapi-user-info");
+
+ // Act
+ var response = await _fixture.HttpClient.SendAsync(request);
+
+ // Assert - REST API returns 401 when identity source is missing
+ Assert.True(
+ response.StatusCode == HttpStatusCode.Unauthorized || response.StatusCode == HttpStatusCode.Forbidden,
+ $"Expected 401 or 403, but got {(int)response.StatusCode} {response.StatusCode}");
+ }
+}
diff --git a/Libraries/test/TestCustomAuthorizerApp/AuthorizerFunction.cs b/Libraries/test/TestCustomAuthorizerApp/AuthorizerFunction.cs
index 27e369893..d0aa91f17 100644
--- a/Libraries/test/TestCustomAuthorizerApp/AuthorizerFunction.cs
+++ b/Libraries/test/TestCustomAuthorizerApp/AuthorizerFunction.cs
@@ -290,6 +290,69 @@ public APIGatewayCustomAuthorizerResponse RestApiAuthorize(
};
}
+ ///
+ /// HTTP API Lambda Authorizer using the simplified IAuthorizerResult pattern.
+ /// Demonstrates how FromHeader extracts the Authorization header and IAuthorizerResult
+ /// abstracts away the raw API Gateway response types.
+ ///
+ [LambdaFunction(ResourceName = "SimpleAuthorizer")]
+ [HttpApiAuthorizer(
+ EnableSimpleResponses = true,
+ AuthorizerPayloadFormatVersion = AuthorizerPayloadFormatVersion.V2)]
+ public IAuthorizerResult SimpleHttpApiAuthorize(
+ [FromHeader(Name = "Authorization")] string authorization,
+ ILambdaContext context)
+ {
+ context.Logger.LogLine($"Simple Authorizer invoked with authorization: {authorization}");
+
+ if (string.IsNullOrEmpty(authorization))
+ {
+ return AuthorizerResults.Deny();
+ }
+
+ if (ValidTokens.Contains(authorization))
+ {
+ return AuthorizerResults.Allow()
+ .WithContext("userId", "user-12345")
+ .WithContext("tenantId", "42")
+ .WithContext("userRole", "admin")
+ .WithContext("email", "test@example.com");
+ }
+
+ return AuthorizerResults.Deny();
+ }
+
+ ///
+ /// REST API Lambda Authorizer using the simplified IAuthorizerResult pattern.
+ /// The generator auto-builds the IAM policy document from the Allow/Deny result.
+ ///
+ [LambdaFunction(ResourceName = "SimpleRestAuthorizer")]
+ [RestApiAuthorizer(
+ Type = RestApiAuthorizerType.Token,
+ IdentityHeader = "Authorization")]
+ public IAuthorizerResult SimpleRestApiAuthorize(
+ [FromHeader(Name = "Authorization")] string authorization,
+ ILambdaContext context)
+ {
+ context.Logger.LogLine($"Simple REST Authorizer invoked with authorization: {authorization}");
+
+ if (string.IsNullOrEmpty(authorization))
+ {
+ return AuthorizerResults.Deny();
+ }
+
+ if (ValidTokens.Contains(authorization))
+ {
+ return AuthorizerResults.Allow()
+ .WithPrincipalId("user-12345")
+ .WithContext("userId", "user-12345")
+ .WithContext("tenantId", "42")
+ .WithContext("email", "test@example.com");
+ }
+
+ return AuthorizerResults.Deny();
+ }
+
private APIGatewayCustomAuthorizerResponse GenerateDenyPolicy(string principalId, string methodArn)
{
return new APIGatewayCustomAuthorizerResponse
@@ -310,4 +373,4 @@ private APIGatewayCustomAuthorizerResponse GenerateDenyPolicy(string principalId
}
};
}
-}
\ No newline at end of file
+}
diff --git a/Libraries/test/TestCustomAuthorizerApp/ProtectedFunction.cs b/Libraries/test/TestCustomAuthorizerApp/ProtectedFunction.cs
index 0a68532f9..c64dc9c68 100644
--- a/Libraries/test/TestCustomAuthorizerApp/ProtectedFunction.cs
+++ b/Libraries/test/TestCustomAuthorizerApp/ProtectedFunction.cs
@@ -191,6 +191,56 @@ public IHttpResult GetIHttpResult(
});
}
+ ///
+ /// HTTP API v2 endpoint protected by the IAuthorizerResult-based simple HTTP API authorizer.
+ /// This tests the end-to-end flow: IAuthorizerResult.Serialize() → API Gateway accepts →
+ /// protected endpoint extracts context values via [FromCustomAuthorizer].
+ ///
+ [LambdaFunction(ResourceName = "SimpleHttpApiUserInfo")]
+ [HttpApi(LambdaHttpMethod.Get, "/api/simple-httpapi-user-info", Authorizer = nameof(AuthorizerFunction.SimpleHttpApiAuthorize))]
+ public object GetSimpleHttpApiUserInfo(
+ [FromCustomAuthorizer(Name = "userId")] string userId,
+ [FromCustomAuthorizer(Name = "email")] string email,
+ [FromCustomAuthorizer(Name = "tenantId")] string tenantId,
+ ILambdaContext context)
+ {
+ context.Logger.LogLine($"[Simple HTTP API] Getting user info for: {userId}");
+
+ return new
+ {
+ UserId = userId,
+ Email = email,
+ TenantId = tenantId,
+ ApiType = "Simple HTTP API (IAuthorizerResult)",
+ Message = "This data came from an IAuthorizerResult-based HTTP API authorizer!"
+ };
+ }
+
+ ///
+ /// REST API endpoint protected by the IAuthorizerResult-based REST API authorizer.
+ /// This tests the end-to-end flow: IAuthorizerResult.Serialize() produces IAM policy →
+ /// API Gateway accepts → protected endpoint extracts context values via [FromCustomAuthorizer].
+ ///
+ [LambdaFunction(ResourceName = "SimpleRestApiUserInfo")]
+ [RestApi(LambdaHttpMethod.Get, "/api/simple-restapi-user-info", Authorizer = nameof(AuthorizerFunction.SimpleRestApiAuthorize))]
+ public object GetSimpleRestApiUserInfo(
+ [FromCustomAuthorizer(Name = "userId")] string userId,
+ [FromCustomAuthorizer(Name = "email")] string email,
+ [FromCustomAuthorizer(Name = "tenantId")] string tenantId,
+ ILambdaContext context)
+ {
+ context.Logger.LogLine($"[Simple REST API] Getting user info for: {userId}");
+
+ return new
+ {
+ UserId = userId,
+ Email = email,
+ TenantId = tenantId,
+ ApiType = "Simple REST API (IAuthorizerResult)",
+ Message = "This data came from an IAuthorizerResult-based REST API authorizer!"
+ };
+ }
+
///
/// HTTP API v2 endpoint demonstrating [FromCustomAuthorizer] with non-string types.
/// Tests that int, bool, and double values are correctly extracted and converted from
diff --git a/Libraries/test/TestCustomAuthorizerApp/aws-lambda-tools-defaults.json b/Libraries/test/TestCustomAuthorizerApp/aws-lambda-tools-defaults.json
index 53198ee82..3755e9a5e 100644
--- a/Libraries/test/TestCustomAuthorizerApp/aws-lambda-tools-defaults.json
+++ b/Libraries/test/TestCustomAuthorizerApp/aws-lambda-tools-defaults.json
@@ -8,8 +8,8 @@
"configuration": "Release",
"template": "serverless.template",
"template-parameters": "",
- "s3-bucket": "test-custom-authorizer-app",
+"s3-bucket" : "test-custom-authorizer-df92b05d",
"s3-prefix": "TestCustomAuthorizerApp/",
- "stack-name": "test-custom-authorizer",
- "function-architecture": "x86_64"
-}
\ No newline at end of file
+"stack-name" : "test-custom-authorizer-df92b05d",
+"function-architecture" : "x86_64"
+}
diff --git a/Libraries/test/TestCustomAuthorizerApp/serverless.template b/Libraries/test/TestCustomAuthorizerApp/serverless.template
index f1714a728..59a8256fb 100644
--- a/Libraries/test/TestCustomAuthorizerApp/serverless.template
+++ b/Libraries/test/TestCustomAuthorizerApp/serverless.template
@@ -44,6 +44,23 @@
},
"EnableFunctionDefaultPermissions": true,
"AuthorizerResultTtlInSeconds": 0
+ },
+ "SimpleHttpApiAuthorize": {
+ "FunctionArn": {
+ "Fn::GetAtt": [
+ "SimpleAuthorizer",
+ "Arn"
+ ]
+ },
+ "AuthorizerPayloadFormatVersion": "2.0",
+ "EnableSimpleResponses": true,
+ "Identity": {
+ "Headers": [
+ "Authorization"
+ ]
+ },
+ "EnableFunctionDefaultPermissions": true,
+ "AuthorizerResultTtlInSeconds": 0
}
}
}
@@ -65,7 +82,21 @@
"Identity": {
"Header": "Authorization"
},
- "FunctionPayloadType": "TOKEN"
+ "FunctionPayloadType": "TOKEN",
+ "AuthorizerResultTtlInSeconds": 0
+ },
+ "SimpleRestApiAuthorize": {
+ "FunctionArn": {
+ "Fn::GetAtt": [
+ "SimpleRestAuthorizer",
+ "Arn"
+ ]
+ },
+ "Identity": {
+ "Header": "Authorization"
+ },
+ "FunctionPayloadType": "TOKEN",
+ "AuthorizerResultTtlInSeconds": 0
}
}
}
@@ -91,6 +122,23 @@
"Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.AuthorizerFunction_HttpApiAuthorize_Generated::HttpApiAuthorize"
}
},
+ "CustomAuthorizerV1": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.AuthorizerFunction_HttpApiAuthorizeV1_Generated::HttpApiAuthorizeV1"
+ }
+ },
"RestApiAuthorizer": {
"Type": "AWS::Serverless::Function",
"Metadata": {
@@ -108,6 +156,40 @@
"Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.AuthorizerFunction_RestApiAuthorize_Generated::RestApiAuthorize"
}
},
+ "SimpleAuthorizer": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.AuthorizerFunction_SimpleHttpApiAuthorize_Generated::SimpleHttpApiAuthorize"
+ }
+ },
+ "SimpleRestAuthorizer": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.AuthorizerFunction_SimpleRestApiAuthorize_Generated::SimpleRestApiAuthorize"
+ }
+ },
"ProtectedEndpoint": {
"Type": "AWS::Serverless::Function",
"Metadata": {
@@ -364,7 +446,7 @@
}
}
},
- "NonStringUserInfo": {
+ "SimpleHttpApiUserInfo": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations",
@@ -389,15 +471,15 @@
"AWSLambdaBasicExecutionRole"
],
"PackageType": "Zip",
- "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.ProtectedFunction_GetNonStringUserInfo_Generated::GetNonStringUserInfo",
+ "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.ProtectedFunction_GetSimpleHttpApiUserInfo_Generated::GetSimpleHttpApiUserInfo",
"Events": {
"RootGet": {
"Type": "HttpApi",
"Properties": {
- "Path": "/api/nonstring-user-info",
+ "Path": "/api/simple-httpapi-user-info",
"Method": "GET",
"Auth": {
- "Authorizer": "HttpApiAuthorize"
+ "Authorizer": "SimpleHttpApiAuthorize"
},
"ApiId": {
"Ref": "AnnotationsHttpApi"
@@ -407,10 +489,21 @@
}
}
},
- "CustomAuthorizerV1": {
+ "SimpleRestApiUserInfo": {
"Type": "AWS::Serverless::Function",
"Metadata": {
- "Tool": "Amazon.Lambda.Annotations"
+ "Tool": "Amazon.Lambda.Annotations",
+ "SyncedEvents": [
+ "RootGet"
+ ],
+ "SyncedEventProperties": {
+ "RootGet": [
+ "Path",
+ "Method",
+ "Auth.Authorizer",
+ "RestApiId.Ref"
+ ]
+ }
},
"Properties": {
"Runtime": "dotnet6",
@@ -421,7 +514,65 @@
"AWSLambdaBasicExecutionRole"
],
"PackageType": "Zip",
- "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.AuthorizerFunction_HttpApiAuthorizeV1_Generated::HttpApiAuthorizeV1"
+ "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.ProtectedFunction_GetSimpleRestApiUserInfo_Generated::GetSimpleRestApiUserInfo",
+ "Events": {
+ "RootGet": {
+ "Type": "Api",
+ "Properties": {
+ "Path": "/api/simple-restapi-user-info",
+ "Method": "GET",
+ "Auth": {
+ "Authorizer": "SimpleRestApiAuthorize"
+ },
+ "RestApiId": {
+ "Ref": "AnnotationsRestApi"
+ }
+ }
+ }
+ }
+ }
+ },
+ "NonStringUserInfo": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations",
+ "SyncedEvents": [
+ "RootGet"
+ ],
+ "SyncedEventProperties": {
+ "RootGet": [
+ "Path",
+ "Method",
+ "Auth.Authorizer",
+ "ApiId.Ref"
+ ]
+ }
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.ProtectedFunction_GetNonStringUserInfo_Generated::GetNonStringUserInfo",
+ "Events": {
+ "RootGet": {
+ "Type": "HttpApi",
+ "Properties": {
+ "Path": "/api/nonstring-user-info",
+ "Method": "GET",
+ "Auth": {
+ "Authorizer": "HttpApiAuthorize"
+ },
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
+ }
+ }
+ }
}
}
}
diff --git a/Libraries/test/TestCustomAuthorizerApp/src/Function/serverless.template b/Libraries/test/TestCustomAuthorizerApp/src/Function/serverless.template
index efd73d7fd..8ab74a5eb 100644
--- a/Libraries/test/TestCustomAuthorizerApp/src/Function/serverless.template
+++ b/Libraries/test/TestCustomAuthorizerApp/src/Function/serverless.template
@@ -11,7 +11,7 @@
"Properties": {
"Auth": {
"Authorizers": {
- "HttpApiLambdaAuthorizer": {
+ "HttpApiAuthorize": {
"FunctionArn": {
"Fn::GetAtt": [
"CustomAuthorizer",
@@ -28,7 +28,7 @@
"EnableFunctionDefaultPermissions": true,
"AuthorizerResultTtlInSeconds": 0
},
- "HttpApiLambdaAuthorizerV1": {
+ "HttpApiAuthorizeV1": {
"FunctionArn": {
"Fn::GetAtt": [
"CustomAuthorizerV1",
@@ -44,6 +44,23 @@
},
"EnableFunctionDefaultPermissions": true,
"AuthorizerResultTtlInSeconds": 0
+ },
+ "SimpleHttpApiAuthorize": {
+ "FunctionArn": {
+ "Fn::GetAtt": [
+ "SimpleAuthorizer",
+ "Arn"
+ ]
+ },
+ "AuthorizerPayloadFormatVersion": "2.0",
+ "EnableSimpleResponses": true,
+ "Identity": {
+ "Headers": [
+ "Authorization"
+ ]
+ },
+ "EnableFunctionDefaultPermissions": true,
+ "AuthorizerResultTtlInSeconds": 0
}
}
}
@@ -55,7 +72,7 @@
"StageName": "Prod",
"Auth": {
"Authorizers": {
- "RestApiLambdaAuthorizer": {
+ "RestApiAuthorize": {
"FunctionArn": {
"Fn::GetAtt": [
"RestApiAuthorizer",
@@ -65,7 +82,21 @@
"Identity": {
"Header": "Authorization"
},
- "FunctionPayloadType": "TOKEN"
+ "FunctionPayloadType": "TOKEN",
+ "AuthorizerResultTtlInSeconds": 0
+ },
+ "SimpleRestApiAuthorize": {
+ "FunctionArn": {
+ "Fn::GetAtt": [
+ "SimpleRestAuthorizer",
+ "Arn"
+ ]
+ },
+ "Identity": {
+ "Header": "Authorization"
+ },
+ "FunctionPayloadType": "TOKEN",
+ "AuthorizerResultTtlInSeconds": 0
}
}
}
@@ -107,7 +138,7 @@
"Path": "/api/protected",
"Method": "GET",
"Auth": {
- "Authorizer": "HttpApiLambdaAuthorizer"
+ "Authorizer": "HttpApiAuthorize"
},
"ApiId": {
"Ref": "AnnotationsHttpApi"
@@ -150,7 +181,7 @@
"Path": "/api/user-info",
"Method": "GET",
"Auth": {
- "Authorizer": "HttpApiLambdaAuthorizer"
+ "Authorizer": "HttpApiAuthorize"
},
"ApiId": {
"Ref": "AnnotationsHttpApi"
@@ -232,7 +263,7 @@
"Path": "/api/rest-user-info",
"Method": "GET",
"Auth": {
- "Authorizer": "RestApiLambdaAuthorizer"
+ "Authorizer": "RestApiAuthorize"
},
"RestApiId": {
"Ref": "AnnotationsRestApi"
@@ -277,7 +308,7 @@
"Method": "GET",
"PayloadFormatVersion": "1.0",
"Auth": {
- "Authorizer": "HttpApiLambdaAuthorizerV1"
+ "Authorizer": "HttpApiAuthorizeV1"
},
"ApiId": {
"Ref": "AnnotationsHttpApi"
@@ -320,7 +351,7 @@
"Path": "/api/ihttpresult-user-info",
"Method": "GET",
"Auth": {
- "Authorizer": "HttpApiLambdaAuthorizer"
+ "Authorizer": "HttpApiAuthorize"
},
"ApiId": {
"Ref": "AnnotationsHttpApi"
@@ -363,7 +394,7 @@
"Path": "/api/nonstring-user-info",
"Method": "GET",
"Auth": {
- "Authorizer": "HttpApiLambdaAuthorizer"
+ "Authorizer": "HttpApiAuthorize"
},
"ApiId": {
"Ref": "AnnotationsHttpApi"
@@ -423,6 +454,126 @@
"PackageType": "Zip",
"Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.AuthorizerFunction_HttpApiAuthorizeV1_Generated::HttpApiAuthorizeV1"
}
+ },
+ "SimpleHttpApiUserInfo": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations",
+ "SyncedEvents": [
+ "RootGet"
+ ],
+ "SyncedEventProperties": {
+ "RootGet": [
+ "Path",
+ "Method",
+ "Auth.Authorizer",
+ "ApiId.Ref"
+ ]
+ }
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.ProtectedFunction_GetSimpleHttpApiUserInfo_Generated::GetSimpleHttpApiUserInfo",
+ "Events": {
+ "RootGet": {
+ "Type": "HttpApi",
+ "Properties": {
+ "Path": "/api/simple-httpapi-user-info",
+ "Method": "GET",
+ "Auth": {
+ "Authorizer": "SimpleHttpApiAuthorize"
+ },
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
+ }
+ }
+ }
+ }
+ },
+ "SimpleRestApiUserInfo": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations",
+ "SyncedEvents": [
+ "RootGet"
+ ],
+ "SyncedEventProperties": {
+ "RootGet": [
+ "Path",
+ "Method",
+ "Auth.Authorizer",
+ "RestApiId.Ref"
+ ]
+ }
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.ProtectedFunction_GetSimpleRestApiUserInfo_Generated::GetSimpleRestApiUserInfo",
+ "Events": {
+ "RootGet": {
+ "Type": "Api",
+ "Properties": {
+ "Path": "/api/simple-restapi-user-info",
+ "Method": "GET",
+ "Auth": {
+ "Authorizer": "SimpleRestApiAuthorize"
+ },
+ "RestApiId": {
+ "Ref": "AnnotationsRestApi"
+ }
+ }
+ }
+ }
+ }
+ },
+ "SimpleAuthorizer": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.AuthorizerFunction_SimpleHttpApiAuthorize_Generated::SimpleHttpApiAuthorize"
+ }
+ },
+ "SimpleRestAuthorizer": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Runtime": "dotnet6",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestCustomAuthorizerApp::TestCustomAuthorizerApp.AuthorizerFunction_SimpleRestApiAuthorize_Generated::SimpleRestApiAuthorize"
+ }
}
}
}
\ No newline at end of file
diff --git a/Libraries/test/TestExecutableServerlessApp/serverless.template b/Libraries/test/TestExecutableServerlessApp/serverless.template
index ac43959b7..a16b62e5e 100644
--- a/Libraries/test/TestExecutableServerlessApp/serverless.template
+++ b/Libraries/test/TestExecutableServerlessApp/serverless.template
@@ -22,7 +22,7 @@
}
},
"Resources": {
- "TestServerlessAppCustomizeResponseExamplesOkResponseWithHeaderGenerated": {
+ "GreeterSayHello": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations",
@@ -32,12 +32,13 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "PayloadFormatVersion"
]
}
},
"Properties": {
- "MemorySize": 512,
+ "MemorySize": 1024,
"Timeout": 30,
"Policies": [
"AWSLambdaBasicExecutionRole"
@@ -51,21 +52,22 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "OkResponseWithHeader"
+ "ANNOTATIONS_HANDLER": "SayHello"
}
},
"Events": {
"RootGet": {
- "Type": "Api",
+ "Type": "HttpApi",
"Properties": {
- "Path": "/okresponsewithheader/{x}",
- "Method": "GET"
+ "Path": "/Greeter/SayHello",
+ "Method": "GET",
+ "PayloadFormatVersion": "1.0"
}
}
}
}
},
- "TestServerlessAppCustomizeResponseExamplesOkResponseWithHeaderAsyncGenerated": {
+ "GreeterSayHelloAsync": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations",
@@ -75,13 +77,14 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "PayloadFormatVersion"
]
}
},
"Properties": {
"MemorySize": 512,
- "Timeout": 30,
+ "Timeout": 50,
"Policies": [
"AWSLambdaBasicExecutionRole"
],
@@ -94,33 +97,47 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "OkResponseWithHeaderAsync"
+ "ANNOTATIONS_HANDLER": "SayHelloAsync"
}
},
"Events": {
"RootGet": {
- "Type": "Api",
+ "Type": "HttpApi",
"Properties": {
- "Path": "/okresponsewithheaderasync/{x}",
- "Method": "GET"
+ "Path": "/Greeter/SayHelloAsync",
+ "Method": "GET",
+ "PayloadFormatVersion": "1.0"
}
}
}
}
},
- "TestServerlessAppCustomizeResponseExamplesNotFoundResponseWithHeaderV2Generated": {
+ "TestServerlessAppParameterlessMethodWithResponseNoParameterWithResponseGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
- "Tool": "Amazon.Lambda.Annotations",
- "SyncedEvents": [
- "RootGet"
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Runtime": "provided.al2",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
],
- "SyncedEventProperties": {
- "RootGet": [
- "Path",
- "Method"
- ]
+ "PackageType": "Zip",
+ "Handler": "TestExecutableServerlessApp",
+ "Environment": {
+ "Variables": {
+ "ANNOTATIONS_HANDLER": "NoParameterWithResponse"
+ }
}
+ }
+ },
+ "ToUpper": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
},
"Properties": {
"MemorySize": 512,
@@ -137,21 +154,12 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "NotFoundResponseWithHeaderV2"
- }
- },
- "Events": {
- "RootGet": {
- "Type": "HttpApi",
- "Properties": {
- "Path": "/notfoundwithheaderv2/{x}",
- "Method": "GET"
- }
+ "ANNOTATIONS_HANDLER": "ToUpper"
}
}
}
},
- "TestServerlessAppCustomizeResponseExamplesNotFoundResponseWithHeaderV2AsyncGenerated": {
+ "TestExecutableServerlessAppSourceGenerationSerializationExampleGetPersonGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations",
@@ -180,21 +188,68 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "NotFoundResponseWithHeaderV2Async"
+ "ANNOTATIONS_HANDLER": "GetPerson"
}
},
"Events": {
"RootGet": {
- "Type": "HttpApi",
+ "Type": "Api",
"Properties": {
- "Path": "/notfoundwithheaderv2async/{x}",
+ "Path": "/",
"Method": "GET"
}
}
}
}
},
- "TestServerlessAppCustomizeResponseExamplesNotFoundResponseWithHeaderV1Generated": {
+ "ToLower": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Runtime": "provided.al2",
+ "CodeUri": ".",
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Zip",
+ "Handler": "TestExecutableServerlessApp",
+ "Environment": {
+ "Variables": {
+ "ANNOTATIONS_HANDLER": "ToLower"
+ }
+ }
+ }
+ },
+ "TestServerlessAppIntrinsicExampleHasIntrinsicGenerated": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Image",
+ "ImageUri": ".",
+ "ImageConfig": {
+ "Command": [
+ "TestExecutableServerlessApp"
+ ]
+ },
+ "Environment": {
+ "Variables": {
+ "ANNOTATIONS_HANDLER": "HasIntrinsic"
+ }
+ }
+ }
+ },
+ "TestServerlessAppCustomizeResponseExamplesOkResponseWithHeaderGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations",
@@ -204,8 +259,7 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method",
- "PayloadFormatVersion"
+ "Method"
]
}
},
@@ -224,22 +278,21 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "NotFoundResponseWithHeaderV1"
+ "ANNOTATIONS_HANDLER": "OkResponseWithHeader"
}
},
"Events": {
"RootGet": {
- "Type": "HttpApi",
+ "Type": "Api",
"Properties": {
- "Path": "/notfoundwithheaderv1/{x}",
- "Method": "GET",
- "PayloadFormatVersion": "1.0"
+ "Path": "/okresponsewithheader/{x}",
+ "Method": "GET"
}
}
}
}
},
- "TestServerlessAppCustomizeResponseExamplesNotFoundResponseWithHeaderV1AsyncGenerated": {
+ "TestServerlessAppCustomizeResponseExamplesOkResponseWithHeaderAsyncGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations",
@@ -249,8 +302,7 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method",
- "PayloadFormatVersion"
+ "Method"
]
}
},
@@ -269,25 +321,33 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "NotFoundResponseWithHeaderV1Async"
+ "ANNOTATIONS_HANDLER": "OkResponseWithHeaderAsync"
}
},
"Events": {
"RootGet": {
- "Type": "HttpApi",
+ "Type": "Api",
"Properties": {
- "Path": "/notfoundwithheaderv1async/{x}",
- "Method": "GET",
- "PayloadFormatVersion": "1.0"
+ "Path": "/okresponsewithheaderasync/{x}",
+ "Method": "GET"
}
}
}
}
},
- "TestServerlessAppDynamicExampleDynamicReturnGenerated": {
+ "TestServerlessAppCustomizeResponseExamplesNotFoundResponseWithHeaderV2Generated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
- "Tool": "Amazon.Lambda.Annotations"
+ "Tool": "Amazon.Lambda.Annotations",
+ "SyncedEvents": [
+ "RootGet"
+ ],
+ "SyncedEventProperties": {
+ "RootGet": [
+ "Path",
+ "Method"
+ ]
+ }
},
"Properties": {
"MemorySize": 512,
@@ -304,15 +364,33 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "DynamicReturn"
+ "ANNOTATIONS_HANDLER": "NotFoundResponseWithHeaderV2"
+ }
+ },
+ "Events": {
+ "RootGet": {
+ "Type": "HttpApi",
+ "Properties": {
+ "Path": "/notfoundwithheaderv2/{x}",
+ "Method": "GET"
+ }
}
}
}
},
- "TestServerlessAppDynamicExampleDynamicInputGenerated": {
+ "TestServerlessAppCustomizeResponseExamplesNotFoundResponseWithHeaderV2AsyncGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
- "Tool": "Amazon.Lambda.Annotations"
+ "Tool": "Amazon.Lambda.Annotations",
+ "SyncedEvents": [
+ "RootGet"
+ ],
+ "SyncedEventProperties": {
+ "RootGet": [
+ "Path",
+ "Method"
+ ]
+ }
},
"Properties": {
"MemorySize": 512,
@@ -329,12 +407,21 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "DynamicInput"
+ "ANNOTATIONS_HANDLER": "NotFoundResponseWithHeaderV2Async"
+ }
+ },
+ "Events": {
+ "RootGet": {
+ "Type": "HttpApi",
+ "Properties": {
+ "Path": "/notfoundwithheaderv2async/{x}",
+ "Method": "GET"
+ }
}
}
}
},
- "GreeterSayHello": {
+ "TestServerlessAppCustomizeResponseExamplesNotFoundResponseWithHeaderV1Generated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations",
@@ -350,7 +437,7 @@
}
},
"Properties": {
- "MemorySize": 1024,
+ "MemorySize": 512,
"Timeout": 30,
"Policies": [
"AWSLambdaBasicExecutionRole"
@@ -364,14 +451,14 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "SayHello"
+ "ANNOTATIONS_HANDLER": "NotFoundResponseWithHeaderV1"
}
},
"Events": {
"RootGet": {
"Type": "HttpApi",
"Properties": {
- "Path": "/Greeter/SayHello",
+ "Path": "/notfoundwithheaderv1/{x}",
"Method": "GET",
"PayloadFormatVersion": "1.0"
}
@@ -379,7 +466,7 @@
}
}
},
- "GreeterSayHelloAsync": {
+ "TestServerlessAppCustomizeResponseExamplesNotFoundResponseWithHeaderV1AsyncGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations",
@@ -396,7 +483,7 @@
},
"Properties": {
"MemorySize": 512,
- "Timeout": 50,
+ "Timeout": 30,
"Policies": [
"AWSLambdaBasicExecutionRole"
],
@@ -409,14 +496,14 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "SayHelloAsync"
+ "ANNOTATIONS_HANDLER": "NotFoundResponseWithHeaderV1Async"
}
},
"Events": {
"RootGet": {
"Type": "HttpApi",
"Properties": {
- "Path": "/Greeter/SayHelloAsync",
+ "Path": "/notfoundwithheaderv1async/{x}",
"Method": "GET",
"PayloadFormatVersion": "1.0"
}
@@ -424,7 +511,7 @@
}
}
},
- "TestServerlessAppIntrinsicExampleHasIntrinsicGenerated": {
+ "TestServerlessAppVoidExampleVoidReturnGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations"
@@ -444,7 +531,7 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "HasIntrinsic"
+ "ANNOTATIONS_HANDLER": "VoidReturn"
}
}
}
@@ -492,64 +579,11 @@
}
}
},
- "TestServerlessAppParameterlessMethodsNoParameterGenerated": {
- "Type": "AWS::Serverless::Function",
- "Metadata": {
- "Tool": "Amazon.Lambda.Annotations"
- },
- "Properties": {
- "Runtime": "provided.al2",
- "CodeUri": ".",
- "MemorySize": 512,
- "Timeout": 30,
- "Policies": [
- "AWSLambdaBasicExecutionRole"
- ],
- "PackageType": "Zip",
- "Handler": "TestExecutableServerlessApp",
- "Environment": {
- "Variables": {
- "ANNOTATIONS_HANDLER": "NoParameter"
- }
- }
- }
- },
- "TestServerlessAppParameterlessMethodWithResponseNoParameterWithResponseGenerated": {
+ "TestServerlessAppDynamicExampleDynamicReturnGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations"
},
- "Properties": {
- "Runtime": "provided.al2",
- "CodeUri": ".",
- "MemorySize": 512,
- "Timeout": 30,
- "Policies": [
- "AWSLambdaBasicExecutionRole"
- ],
- "PackageType": "Zip",
- "Handler": "TestExecutableServerlessApp",
- "Environment": {
- "Variables": {
- "ANNOTATIONS_HANDLER": "NoParameterWithResponse"
- }
- }
- }
- },
- "TestExecutableServerlessAppSourceGenerationSerializationExampleGetPersonGenerated": {
- "Type": "AWS::Serverless::Function",
- "Metadata": {
- "Tool": "Amazon.Lambda.Annotations",
- "SyncedEvents": [
- "RootGet"
- ],
- "SyncedEventProperties": {
- "RootGet": [
- "Path",
- "Method"
- ]
- }
- },
"Properties": {
"MemorySize": 512,
"Timeout": 30,
@@ -565,21 +599,12 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "GetPerson"
- }
- },
- "Events": {
- "RootGet": {
- "Type": "Api",
- "Properties": {
- "Path": "/",
- "Method": "GET"
- }
+ "ANNOTATIONS_HANDLER": "DynamicReturn"
}
}
}
},
- "ToUpper": {
+ "TestServerlessAppDynamicExampleDynamicInputGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations"
@@ -599,12 +624,12 @@
},
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "ToUpper"
+ "ANNOTATIONS_HANDLER": "DynamicInput"
}
}
}
},
- "ToLower": {
+ "TestServerlessAppParameterlessMethodsNoParameterGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
"Tool": "Amazon.Lambda.Annotations"
@@ -621,7 +646,7 @@
"Handler": "TestExecutableServerlessApp",
"Environment": {
"Variables": {
- "ANNOTATIONS_HANDLER": "ToLower"
+ "ANNOTATIONS_HANDLER": "NoParameter"
}
}
}
@@ -650,31 +675,6 @@
}
}
}
- },
- "TestServerlessAppVoidExampleVoidReturnGenerated": {
- "Type": "AWS::Serverless::Function",
- "Metadata": {
- "Tool": "Amazon.Lambda.Annotations"
- },
- "Properties": {
- "MemorySize": 512,
- "Timeout": 30,
- "Policies": [
- "AWSLambdaBasicExecutionRole"
- ],
- "PackageType": "Image",
- "ImageUri": ".",
- "ImageConfig": {
- "Command": [
- "TestExecutableServerlessApp"
- ]
- },
- "Environment": {
- "Variables": {
- "ANNOTATIONS_HANDLER": "VoidReturn"
- }
- }
- }
}
},
"Outputs": {
diff --git a/Libraries/test/TestServerlessApp.IntegrationTests/IntegrationTestContextFixture.cs b/Libraries/test/TestServerlessApp.IntegrationTests/IntegrationTestContextFixture.cs
index a9abea041..fb689cd7c 100644
--- a/Libraries/test/TestServerlessApp.IntegrationTests/IntegrationTestContextFixture.cs
+++ b/Libraries/test/TestServerlessApp.IntegrationTests/IntegrationTestContextFixture.cs
@@ -64,11 +64,11 @@ public async Task InitializeAsync()
// since the serverless.template is managed by the source generator and may not have Outputs
var region = "us-west-2";
Console.WriteLine($"[IntegrationTest] Querying stack resources for '{_stackName}'...");
- var httpApiId = await _cloudFormationHelper.GetResourcePhysicalIdAsync(_stackName, "ServerlessHttpApi");
- var restApiId = await _cloudFormationHelper.GetResourcePhysicalIdAsync(_stackName, "ServerlessRestApi");
- Console.WriteLine($"[IntegrationTest] ServerlessHttpApi: {httpApiId}, ServerlessRestApi: {restApiId}");
- Assert.False(string.IsNullOrEmpty(httpApiId), $"CloudFormation resource 'ServerlessHttpApi' was not found or has an empty physical ID for stack '{_stackName}'.");
- Assert.False(string.IsNullOrEmpty(restApiId), $"CloudFormation resource 'ServerlessRestApi' was not found or has an empty physical ID for stack '{_stackName}'.");
+ var httpApiId = await _cloudFormationHelper.GetResourcePhysicalIdAsync(_stackName, "AnnotationsHttpApi");
+ var restApiId = await _cloudFormationHelper.GetResourcePhysicalIdAsync(_stackName, "AnnotationsRestApi");
+ Console.WriteLine($"[IntegrationTest] AnnotationsHttpApi: {httpApiId}, AnnotationsRestApi: {restApiId}");
+ Assert.False(string.IsNullOrEmpty(httpApiId), $"CloudFormation resource 'AnnotationsHttpApi' was not found or has an empty physical ID for stack '{_stackName}'.");
+ Assert.False(string.IsNullOrEmpty(restApiId), $"CloudFormation resource 'AnnotationsRestApi' was not found or has an empty physical ID for stack '{_stackName}'.");
HttpApiUrlPrefix = $"https://{httpApiId}.execute-api.{region}.amazonaws.com";
RestApiUrlPrefix = $"https://{restApiId}.execute-api.{region}.amazonaws.com/Prod";
@@ -81,7 +81,7 @@ public async Task InitializeAsync()
Console.WriteLine($"[IntegrationTest] Found {LambdaFunctions.Count} Lambda functions: {string.Join(", ", LambdaFunctions.Select(f => f.Name ?? "(null)"))}");
Assert.True(await _s3Helper.BucketExistsAsync(_bucketName), $"S3 bucket {_bucketName} should exist");
- Assert.Equal(34, LambdaFunctions.Count);
+ Assert.Equal(36, LambdaFunctions.Count);
Assert.False(string.IsNullOrEmpty(RestApiUrlPrefix), "RestApiUrlPrefix should not be empty");
Assert.False(string.IsNullOrEmpty(HttpApiUrlPrefix), "HttpApiUrlPrefix should not be empty");
diff --git a/Libraries/test/TestServerlessApp/IAuthorizerResultExample.cs b/Libraries/test/TestServerlessApp/IAuthorizerResultExample.cs
new file mode 100644
index 000000000..98cbb231c
--- /dev/null
+++ b/Libraries/test/TestServerlessApp/IAuthorizerResultExample.cs
@@ -0,0 +1,60 @@
+using Amazon.Lambda.Annotations;
+using Amazon.Lambda.Annotations.APIGateway;
+using Amazon.Lambda.Core;
+
+namespace TestServerlessApp
+{
+ ///
+ /// Examples demonstrating the IAuthorizerResult pattern for Lambda authorizer functions.
+ /// These use FromHeader to extract headers and IAuthorizerResult for simplified Allow/Deny responses.
+ ///
+ public class IAuthorizerResultExample
+ {
+ ///
+ /// HTTP API V2 authorizer using IAuthorizerResult with simple responses.
+ /// The source generator produces a handler that:
+ /// 1. Accepts APIGatewayCustomAuthorizerV2Request
+ /// 2. Extracts the Authorization header via [FromHeader]
+ /// 3. Calls the user method
+ /// 4. Serializes IAuthorizerResult to the simple response format using RouteArn
+ ///
+ [LambdaFunction(ResourceName = "SimpleHttpApiAuth", PackageType = LambdaPackageType.Image)]
+ [HttpApiAuthorizer(
+ EnableSimpleResponses = true,
+ AuthorizerPayloadFormatVersion = AuthorizerPayloadFormatVersion.V2)]
+ public IAuthorizerResult SimpleHttpApiAuthorizer(
+ [FromHeader(Name = "Authorization")] string authorization,
+ ILambdaContext context)
+ {
+ if (string.IsNullOrEmpty(authorization))
+ return AuthorizerResults.Deny();
+
+ return AuthorizerResults.Allow()
+ .WithContext("userId", "user-123");
+ }
+
+ ///
+ /// REST API token authorizer using IAuthorizerResult.
+ /// The source generator produces a handler that:
+ /// 1. Accepts APIGatewayCustomAuthorizerRequest
+ /// 2. Extracts the Authorization header via [FromHeader]
+ /// 3. Calls the user method
+ /// 4. Serializes IAuthorizerResult to IAM policy format using MethodArn
+ ///
+ [LambdaFunction(ResourceName = "SimpleRestApiAuth", PackageType = LambdaPackageType.Image)]
+ [RestApiAuthorizer(
+ Type = RestApiAuthorizerType.Token,
+ IdentityHeader = "Authorization")]
+ public IAuthorizerResult SimpleRestApiAuthorizer(
+ [FromHeader(Name = "Authorization")] string authorization,
+ ILambdaContext context)
+ {
+ if (string.IsNullOrEmpty(authorization))
+ return AuthorizerResults.Deny();
+
+ return AuthorizerResults.Allow()
+ .WithPrincipalId("user-123")
+ .WithContext("userId", "user-123");
+ }
+ }
+}
diff --git a/Libraries/test/TestServerlessApp/aws-lambda-tools-defaults.json b/Libraries/test/TestServerlessApp/aws-lambda-tools-defaults.json
index 2073b4771..5b6cc176c 100644
--- a/Libraries/test/TestServerlessApp/aws-lambda-tools-defaults.json
+++ b/Libraries/test/TestServerlessApp/aws-lambda-tools-defaults.json
@@ -13,7 +13,7 @@
"template": "serverless.template",
"template-parameters": "",
"docker-host-build-output-dir": "./bin/Release/lambda-publish",
-"s3-bucket" : "test-serverless-app-66348a16",
-"stack-name" : "test-serverless-app-66348a16",
+"s3-bucket" : "test-serverless-app-190b4b86",
+"stack-name" : "test-serverless-app-190b4b86",
"function-architecture" : "x86_64"
}
diff --git a/Libraries/test/TestServerlessApp/serverless.template b/Libraries/test/TestServerlessApp/serverless.template
index 8a8ca51ee..5a0288c72 100644
--- a/Libraries/test/TestServerlessApp/serverless.template
+++ b/Libraries/test/TestServerlessApp/serverless.template
@@ -6,6 +6,61 @@
"TestQueue": {
"Type": "AWS::SQS::Queue"
},
+ "AnnotationsHttpApi": {
+ "Type": "AWS::Serverless::HttpApi",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "Auth": {
+ "Authorizers": {
+ "SimpleHttpApiAuthorizer": {
+ "FunctionArn": {
+ "Fn::GetAtt": [
+ "SimpleHttpApiAuth",
+ "Arn"
+ ]
+ },
+ "AuthorizerPayloadFormatVersion": "2.0",
+ "EnableSimpleResponses": true,
+ "Identity": {
+ "Headers": [
+ "Authorization"
+ ]
+ },
+ "EnableFunctionDefaultPermissions": true,
+ "AuthorizerResultTtlInSeconds": 0
+ }
+ }
+ }
+ }
+ },
+ "AnnotationsRestApi": {
+ "Type": "AWS::Serverless::Api",
+ "Properties": {
+ "StageName": "Prod",
+ "Auth": {
+ "Authorizers": {
+ "SimpleRestApiAuthorizer": {
+ "FunctionArn": {
+ "Fn::GetAtt": [
+ "SimpleRestApiAuth",
+ "Arn"
+ ]
+ },
+ "Identity": {
+ "Header": "Authorization"
+ },
+ "FunctionPayloadType": "TOKEN",
+ "AuthorizerResultTtlInSeconds": 0
+ }
+ }
+ }
+ },
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ }
+ },
"AuthNameFallbackTest": {
"Type": "AWS::Serverless::Function",
"Metadata": {
@@ -16,7 +71,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "ApiId.Ref"
]
}
},
@@ -38,7 +94,10 @@
"Type": "HttpApi",
"Properties": {
"Path": "/api/authorizer-fallback",
- "Method": "GET"
+ "Method": "GET",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -54,7 +113,8 @@
"SyncedEventProperties": {
"RootPost": [
"Path",
- "Method"
+ "Method",
+ "ApiId.Ref"
]
}
},
@@ -76,7 +136,10 @@
"Type": "HttpApi",
"Properties": {
"Path": "/ComplexCalculator/Add",
- "Method": "POST"
+ "Method": "POST",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -92,7 +155,8 @@
"SyncedEventProperties": {
"RootPost": [
"Path",
- "Method"
+ "Method",
+ "ApiId.Ref"
]
}
},
@@ -114,7 +178,10 @@
"Type": "HttpApi",
"Properties": {
"Path": "/ComplexCalculator/Subtract",
- "Method": "POST"
+ "Method": "POST",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -130,7 +197,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "ApiId.Ref"
]
}
},
@@ -152,7 +220,10 @@
"Type": "HttpApi",
"Properties": {
"Path": "/api/authorizer",
- "Method": "GET"
+ "Method": "GET",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -169,7 +240,8 @@
"RootGet": [
"Path",
"Method",
- "PayloadFormatVersion"
+ "PayloadFormatVersion",
+ "ApiId.Ref"
]
}
},
@@ -192,7 +264,10 @@
"Properties": {
"Path": "/api/authorizer-v1",
"Method": "GET",
- "PayloadFormatVersion": "1.0"
+ "PayloadFormatVersion": "1.0",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -208,7 +283,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "ApiId.Ref"
]
}
},
@@ -230,7 +306,10 @@
"Type": "HttpApi",
"Properties": {
"Path": "/api/authorizer-non-string",
- "Method": "GET"
+ "Method": "GET",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -246,7 +325,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "RestApiId.Ref"
]
}
},
@@ -268,7 +348,10 @@
"Type": "Api",
"Properties": {
"Path": "/rest/authorizer",
- "Method": "GET"
+ "Method": "GET",
+ "RestApiId": {
+ "Ref": "AnnotationsRestApi"
+ }
}
}
}
@@ -284,7 +367,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "ApiId.Ref"
]
}
},
@@ -306,7 +390,10 @@
"Type": "HttpApi",
"Properties": {
"Path": "/authorizerihttpresults",
- "Method": "GET"
+ "Method": "GET",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -322,7 +409,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "RestApiId.Ref"
]
}
},
@@ -344,7 +432,10 @@
"Type": "Api",
"Properties": {
"Path": "/okresponsewithheader/{x}",
- "Method": "GET"
+ "Method": "GET",
+ "RestApiId": {
+ "Ref": "AnnotationsRestApi"
+ }
}
}
}
@@ -360,7 +451,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "RestApiId.Ref"
]
}
},
@@ -382,7 +474,10 @@
"Type": "Api",
"Properties": {
"Path": "/okresponsewithheaderasync/{x}",
- "Method": "GET"
+ "Method": "GET",
+ "RestApiId": {
+ "Ref": "AnnotationsRestApi"
+ }
}
}
}
@@ -398,7 +493,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "ApiId.Ref"
]
}
},
@@ -420,7 +516,10 @@
"Type": "HttpApi",
"Properties": {
"Path": "/notfoundwithheaderv2/{x}",
- "Method": "GET"
+ "Method": "GET",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -436,7 +535,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "ApiId.Ref"
]
}
},
@@ -458,7 +558,10 @@
"Type": "HttpApi",
"Properties": {
"Path": "/notfoundwithheaderv2async/{x}",
- "Method": "GET"
+ "Method": "GET",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -475,7 +578,8 @@
"RootGet": [
"Path",
"Method",
- "PayloadFormatVersion"
+ "PayloadFormatVersion",
+ "ApiId.Ref"
]
}
},
@@ -498,7 +602,10 @@
"Properties": {
"Path": "/notfoundwithheaderv1/{x}",
"Method": "GET",
- "PayloadFormatVersion": "1.0"
+ "PayloadFormatVersion": "1.0",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -515,7 +622,8 @@
"RootGet": [
"Path",
"Method",
- "PayloadFormatVersion"
+ "PayloadFormatVersion",
+ "ApiId.Ref"
]
}
},
@@ -538,7 +646,10 @@
"Properties": {
"Path": "/notfoundwithheaderv1async/{x}",
"Method": "GET",
- "PayloadFormatVersion": "1.0"
+ "PayloadFormatVersion": "1.0",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -555,7 +666,8 @@
"RootGet": [
"Path",
"Method",
- "PayloadFormatVersion"
+ "PayloadFormatVersion",
+ "ApiId.Ref"
]
}
},
@@ -578,7 +690,10 @@
"Properties": {
"Path": "/okresponsewithcustomserializerasync/{firstName}/{lastName}",
"Method": "GET",
- "PayloadFormatVersion": "1.0"
+ "PayloadFormatVersion": "1.0",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -634,7 +749,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "ApiId.Ref"
]
}
},
@@ -656,7 +772,10 @@
"Type": "HttpApi",
"Properties": {
"Path": "/{text}",
- "Method": "GET"
+ "Method": "GET",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -693,7 +812,8 @@
"RootGet": [
"Path",
"Method",
- "PayloadFormatVersion"
+ "PayloadFormatVersion",
+ "ApiId.Ref"
]
}
},
@@ -716,7 +836,10 @@
"Properties": {
"Path": "/Greeter/SayHello",
"Method": "GET",
- "PayloadFormatVersion": "1.0"
+ "PayloadFormatVersion": "1.0",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -733,7 +856,8 @@
"RootGet": [
"Path",
"Method",
- "PayloadFormatVersion"
+ "PayloadFormatVersion",
+ "ApiId.Ref"
]
}
},
@@ -756,12 +880,55 @@
"Properties": {
"Path": "/Greeter/SayHelloAsync",
"Method": "GET",
- "PayloadFormatVersion": "1.0"
+ "PayloadFormatVersion": "1.0",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
}
},
+ "SimpleHttpApiAuth": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Image",
+ "ImageUri": ".",
+ "ImageConfig": {
+ "Command": [
+ "TestServerlessApp::TestServerlessApp.IAuthorizerResultExample_SimpleHttpApiAuthorizer_Generated::SimpleHttpApiAuthorizer"
+ ]
+ }
+ }
+ },
+ "SimpleRestApiAuth": {
+ "Type": "AWS::Serverless::Function",
+ "Metadata": {
+ "Tool": "Amazon.Lambda.Annotations"
+ },
+ "Properties": {
+ "MemorySize": 512,
+ "Timeout": 30,
+ "Policies": [
+ "AWSLambdaBasicExecutionRole"
+ ],
+ "PackageType": "Image",
+ "ImageUri": ".",
+ "ImageConfig": {
+ "Command": [
+ "TestServerlessApp::TestServerlessApp.IAuthorizerResultExample_SimpleRestApiAuthorizer_Generated::SimpleRestApiAuthorizer"
+ ]
+ }
+ }
+ },
"TestServerlessAppIntrinsicExampleHasIntrinsicGenerated": {
"Type": "AWS::Serverless::Function",
"Metadata": {
@@ -792,7 +959,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "ApiId.Ref"
]
}
},
@@ -814,7 +982,10 @@
"Type": "HttpApi",
"Properties": {
"Path": "/nullableheaderhttpapi",
- "Method": "GET"
+ "Method": "GET",
+ "ApiId": {
+ "Ref": "AnnotationsHttpApi"
+ }
}
}
}
@@ -830,7 +1001,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "RestApiId.Ref"
]
}
},
@@ -852,7 +1024,10 @@
"Type": "Api",
"Properties": {
"Path": "/SimpleCalculator/Add",
- "Method": "GET"
+ "Method": "GET",
+ "RestApiId": {
+ "Ref": "AnnotationsRestApi"
+ }
}
}
}
@@ -868,7 +1043,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "RestApiId.Ref"
]
}
},
@@ -890,7 +1066,10 @@
"Type": "Api",
"Properties": {
"Path": "/SimpleCalculator/Subtract",
- "Method": "GET"
+ "Method": "GET",
+ "RestApiId": {
+ "Ref": "AnnotationsRestApi"
+ }
}
}
}
@@ -906,7 +1085,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "RestApiId.Ref"
]
}
},
@@ -928,7 +1108,10 @@
"Type": "Api",
"Properties": {
"Path": "/SimpleCalculator/Multiply/{x}/{y}",
- "Method": "GET"
+ "Method": "GET",
+ "RestApiId": {
+ "Ref": "AnnotationsRestApi"
+ }
}
}
}
@@ -944,7 +1127,8 @@
"SyncedEventProperties": {
"RootGet": [
"Path",
- "Method"
+ "Method",
+ "RestApiId.Ref"
]
}
},
@@ -966,7 +1150,10 @@
"Type": "Api",
"Properties": {
"Path": "/SimpleCalculator/DivideAsync/{x}/{y}",
- "Method": "GET"
+ "Method": "GET",
+ "RestApiId": {
+ "Ref": "AnnotationsRestApi"
+ }
}
}
}