# Archived: Nova IdP CloudFormation Template (v1.28) > **Archived at v1.29.0** — the active path is `terraform apply` in > `nova-platform-ops`. Deletion is a follow-up after Terraform parity > is verified (REQ-369 AC 3, spec §7.5). This template is read-only > reference; do not modify it. The `nova idp setup --apply` command > now delegates to `terraform apply` (see `nova/idp/setup.py`). This is the verbatim output of `generate_template()` from `core/lambda/nova_idp_cfn.py` (the composition of the DynamoDB snippet from `core/lambda/nova_idp_auth_cfn.py` + the KMS signing key + the three IdP Lambdas + their IAM roles + function URLs). It was the active provisioning path through v1.28; from v1.29 the operator runs `terraform apply` in the `nova-platform-ops` checkout and `nova idp setup --apply` delegates to it. The CFN generation code is retained as read-only reference and emits a `DeprecationWarning` when the CFN fallback path is invoked (terraform absent from PATH). ```json { "Resources": { "NovaUsersTable": { "Type": "AWS::DynamoDB::Table", "Properties": { "TableName": "nova-users", "BillingMode": "PAY_PER_REQUEST", "KeySchema": [ { "AttributeName": "user_id", "KeyType": "HASH" } ], "AttributeDefinitions": [ { "AttributeName": "user_id", "AttributeType": "S" }, { "AttributeName": "email", "AttributeType": "S" } ], "GlobalSecondaryIndexes": [ { "IndexName": "email-index", "KeySchema": [ { "AttributeName": "email", "KeyType": "HASH" } ], "Projection": { "ProjectionType": "ALL" } } ], "PointInTimeRecoverySpecification": { "PointInTimeRecoveryEnabled": true }, "AttributeShape": { "user_id": "String", "email": "String", "password_hash": "String", "owner": "String", "roles": "List", "created_at": "String" } } }, "NovaSessionsTable": { "Type": "AWS::DynamoDB::Table", "Properties": { "TableName": "nova-sessions", "BillingMode": "PAY_PER_REQUEST", "KeySchema": [ { "AttributeName": "session_id", "KeyType": "HASH" } ], "AttributeDefinitions": [ { "AttributeName": "session_id", "AttributeType": "S" }, { "AttributeName": "user_id", "AttributeType": "S" } ], "GlobalSecondaryIndexes": [ { "IndexName": "user_id-index", "KeySchema": [ { "AttributeName": "user_id", "KeyType": "HASH" } ], "Projection": { "ProjectionType": "ALL" } } ], "TimeToLiveSpecification": { "AttributeName": "expires_at", "Enabled": true }, "AttributeShape": { "session_id": "String", "user_id": "String", "expires_at": "String (epoch seconds, TTL)", "created_at": "String (ISO-8601)" } } }, "NovaPasswordResetsTable": { "Type": "AWS::DynamoDB::Table", "Properties": { "TableName": "nova-password-resets", "BillingMode": "PAY_PER_REQUEST", "KeySchema": [ { "AttributeName": "reset_token", "KeyType": "HASH" } ], "AttributeDefinitions": [ { "AttributeName": "reset_token", "AttributeType": "S" } ], "TimeToLiveSpecification": { "AttributeName": "expires_at", "Enabled": true }, "AttributeShape": { "reset_token": "String", "user_id": "String", "expires_at": "String (epoch seconds, TTL; 15 min)" } } }, "NovaPatsTable": { "Type": "AWS::DynamoDB::Table", "Properties": { "TableName": "nova-pats", "BillingMode": "PAY_PER_REQUEST", "KeySchema": [ { "AttributeName": "jti", "KeyType": "HASH" } ], "AttributeDefinitions": [ { "AttributeName": "jti", "AttributeType": "S" }, { "AttributeName": "sub", "AttributeType": "S" }, { "AttributeName": "pat_hash", "AttributeType": "S" } ], "GlobalSecondaryIndexes": [ { "IndexName": "sub-index", "KeySchema": [ { "AttributeName": "sub", "KeyType": "HASH" } ], "Projection": { "ProjectionType": "ALL" } }, { "IndexName": "pat_hash-index", "KeySchema": [ { "AttributeName": "pat_hash", "KeyType": "HASH" } ], "Projection": { "ProjectionType": "ALL" } } ], "TimeToLiveSpecification": { "AttributeName": "expires_at", "Enabled": true }, "AttributeShape": { "jti": "String (PK)", "sub": "String (GSI1; subject / user_id)", "pat_hash": "String (GSI2; SHA-256 of the PAT for lookup)", "status": "String (active|revoked)", "issued_at": "String (ISO-8601)", "expires_at": "String (epoch seconds, TTL)", "revoked_at": "String (ISO-8601, present iff status=revoked)", "claims": "Map (JWT claims payload)" } } }, "NovaOidcSigningKey": { "Type": "AWS::KMS::Key", "Properties": { "Description": "Nova OIDC token signing key (REQ-337, ECC_NIST_P256)", "KeySpec": "ECC_NIST_P256", "KeyUsage": "SIGN_VERIFY", "KeyPolicy": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": { "Fn::Sub": "arn:aws:iam::${AWS::AccountId}:root" } }, "Action": "kms:*", "Resource": "*" } ] } } }, "NovaOidcSigningKeyAlias": { "Type": "AWS::KMS::Alias", "Properties": { "AliasName": "alias/nova-oidc-signing", "TargetKeyId": { "Fn::GetAtt": "NovaOidcSigningKey.Arn" } } }, "NovaIdpAuthRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": { "Fn::Sub": "lambda.${AWS::Region}.amazonaws.com" } }, "Action": "sts:AssumeRole" } ] }, "Policies": [ { "PolicyName": "NovaIdpAuthPolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": { "Fn::Sub": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" } }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup" ], "Resource": { "Fn::Sub": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*" } }, { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:Query", "dynamodb:DeleteItem" ], "Resource": [ { "Fn::Sub": "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/nova-users" }, { "Fn::Sub": "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/nova-sessions" }, { "Fn::Sub": "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/nova-password-resets" } ] } ] } } ] } }, "NovaIdpTokenVendRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": { "Fn::Sub": "lambda.${AWS::Region}.amazonaws.com" } }, "Action": "sts:AssumeRole" } ] }, "Policies": [ { "PolicyName": "NovaIdpTokenVendPolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": { "Fn::Sub": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" } }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup" ], "Resource": { "Fn::Sub": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*" } }, { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:UpdateItem", "dynamodb:Query", "dynamodb:DeleteItem" ], "Resource": [ { "Fn::Sub": "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/nova-pats" } ] }, { "Effect": "Allow", "Action": [ "kms:Sign", "kms:GetPublicKey", "kms:DescribeKey" ], "Resource": { "Fn::GetAtt": "NovaOidcSigningKey.Arn" } } ] } } ] } }, "NovaIdpJwksRole": { "Type": "AWS::IAM::Role", "Properties": { "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": { "Fn::Sub": "lambda.${AWS::Region}.amazonaws.com" } }, "Action": "sts:AssumeRole" } ] }, "Policies": [ { "PolicyName": "NovaIdpJwksPolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": { "Fn::Sub": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" } }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup" ], "Resource": { "Fn::Sub": "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*" } }, { "Effect": "Allow", "Action": [ "kms:Sign", "kms:GetPublicKey", "kms:DescribeKey" ], "Resource": { "Fn::GetAtt": "NovaOidcSigningKey.Arn" } } ] } } ] } }, "NovaIdpAuthFunction": { "Type": "AWS::Lambda::Function", "Properties": { "Handler": "nova_idp_auth.lambda_handler", "Runtime": "python3.12", "MemorySize": 512, "Timeout": 30, "Role": { "Fn::GetAtt": [ "NovaIdpAuthRole", "Arn" ] }, "Environment": { "Variables": { "NOVA_USERS_TABLE": "nova-users", "NOVA_SESSIONS_TABLE": "nova-sessions", "NOVA_PASSWORD_RESETS_TABLE": "nova-password-resets", "NOVA_PATS_TABLE": "nova-pats" } }, "Code": { "ZipFile": "def lambda_handler(event, context):\n return {}" } } }, "NovaIdpTokenVendFunction": { "Type": "AWS::Lambda::Function", "Properties": { "Handler": "nova_idp_token_vend.lambda_handler", "Runtime": "python3.12", "MemorySize": 512, "Timeout": 30, "Role": { "Fn::GetAtt": [ "NovaIdpTokenVendRole", "Arn" ] }, "Environment": { "Variables": { "NOVA_USERS_TABLE": "nova-users", "NOVA_SESSIONS_TABLE": "nova-sessions", "NOVA_PASSWORD_RESETS_TABLE": "nova-password-resets", "NOVA_PATS_TABLE": "nova-pats", "NOVA_OIDC_KMS_KEY_ID": "alias/nova-oidc-signing" } }, "Code": { "ZipFile": "def lambda_handler(event, context):\n return {}" } } }, "NovaIdpJwksFunction": { "Type": "AWS::Lambda::Function", "Properties": { "Handler": "nova_idp_jwks.lambda_handler", "Runtime": "python3.12", "MemorySize": 256, "Timeout": 30, "Role": { "Fn::GetAtt": [ "NovaIdpJwksRole", "Arn" ] }, "Environment": { "Variables": { "NOVA_OIDC_KMS_KEY_ID": "alias/nova-oidc-signing" } }, "Code": { "ZipFile": "def lambda_handler(event, context):\n return {}" } } }, "NovaIdpAuthUrl": { "Type": "AWS::Lambda::Url", "Properties": { "TargetFunction": { "Ref": "NovaIdpAuthFunction" }, "AuthType": "AWS_IAM" } }, "NovaIdpTokenVendUrl": { "Type": "AWS::Lambda::Url", "Properties": { "TargetFunction": { "Ref": "NovaIdpTokenVendFunction" }, "AuthType": "AWS_IAM" } }, "NovaIdpJwksUrl": { "Type": "AWS::Lambda::Url", "Properties": { "TargetFunction": { "Ref": "NovaIdpJwksFunction" }, "AuthType": "NONE" } } } } ```