github.com/avenga/couper@v1.12.2/eval/lib/jwt_test.go (about)

     1  package lib_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/base64"
     6  	"encoding/json"
     7  	"fmt"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/zclconf/go-cty/cty"
    15  	"github.com/zclconf/go-cty/cty/function/stdlib"
    16  
    17  	"github.com/avenga/couper/config/configload"
    18  	"github.com/avenga/couper/config/request"
    19  	"github.com/avenga/couper/errors"
    20  	"github.com/avenga/couper/eval"
    21  	"github.com/avenga/couper/eval/lib"
    22  	"github.com/avenga/couper/internal/test"
    23  )
    24  
    25  func TestJwtSignStatic(t *testing.T) {
    26  	tests := []struct {
    27  		name     string
    28  		hcl      string
    29  		jspLabel string
    30  		claims   string
    31  		want     string
    32  	}{
    33  		{
    34  			"HS256 / key",
    35  			`
    36  			server "test" {
    37  			}
    38  			definitions {
    39  				jwt_signing_profile "MyToken" {
    40  					signature_algorithm = "HS256"
    41  					key = "$3cRe4"
    42  					ttl = "0"
    43  					claims = {
    44  					  iss = to_lower("The_Issuer")
    45  					  aud = to_upper("The_Audience")
    46  					}
    47  				}
    48  			}
    49  			`,
    50  			"MyToken",
    51  			`{"sub":"12345"}`,
    52  			"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.Hf-ZtIlsxR2bDOdAEMaDHaOBmfVWTQi9U68yV4YHW9w",
    53  		},
    54  		{
    55  			"HS256 / key_file",
    56  			`
    57  			server "test" {
    58  			}
    59  			definitions {
    60  				jwt_signing_profile "MyToken" {
    61  					signature_algorithm = "HS256"
    62  					key_file = "testdata/secret.txt"
    63  					ttl = "0"
    64  					claims = {
    65  					  iss = to_lower("The_Issuer")
    66  					  aud = to_upper("The_Audience")
    67  					}
    68  				}
    69  			}
    70  			`,
    71  			"MyToken",
    72  			`{"sub":"12345"}`,
    73  			"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.Hf-ZtIlsxR2bDOdAEMaDHaOBmfVWTQi9U68yV4YHW9w",
    74  		},
    75  		{
    76  			"HS384 / key",
    77  			`
    78  			server "test" {
    79  			}
    80  			definitions {
    81  				jwt_signing_profile "MyToken" {
    82  					signature_algorithm = "HS384"
    83  					key = "$3cRe4"
    84  					ttl = "0"
    85  					claims = {
    86  					  iss = to_lower("The_Issuer")
    87  					  aud = to_upper("The_Audience")
    88  					}
    89  				}
    90  			}
    91  			`,
    92  			"MyToken",
    93  			`{"sub":"12345"}`,
    94  			"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.fYh9VOfe9jv926lwyjNMHr98zqesELs-2v0feqqDByor7rlnHHUdWdZXkTcPRaDa",
    95  		},
    96  		{
    97  			"HS384 / key_file",
    98  			`
    99  			server "test" {
   100  			}
   101  			definitions {
   102  				jwt_signing_profile "MyToken" {
   103  					signature_algorithm = "HS384"
   104  					key_file = "testdata/secret.txt"
   105  					ttl = "0"
   106  					claims = {
   107  					  iss = to_lower("The_Issuer")
   108  					  aud = to_upper("The_Audience")
   109  					}
   110  				}
   111  			}
   112  			`,
   113  			"MyToken",
   114  			`{"sub":"12345"}`,
   115  			"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.fYh9VOfe9jv926lwyjNMHr98zqesELs-2v0feqqDByor7rlnHHUdWdZXkTcPRaDa",
   116  		},
   117  		{
   118  			"HS512 / key",
   119  			`
   120  			server "test" {
   121  			}
   122  			definitions {
   123  				jwt_signing_profile "MyToken" {
   124  					signature_algorithm = "HS512"
   125  					key = "$3cRe4"
   126  					ttl = "0"
   127  					claims = {
   128  					  iss = to_lower("The_Issuer")
   129  					  aud = to_upper("The_Audience")
   130  					}
   131  				}
   132  			}
   133  			`,
   134  			"MyToken",
   135  			`{"sub":"12345"}`,
   136  			"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.LCzGZMYxwLAra2tHNDFBCSKVzMdZeZGxhgGuVr0e9mHDXMqpyOiCBWN2JB-9aDUNPHobwxEWofPY8M9icL3YXQ",
   137  		},
   138  		{
   139  			"HS512 / key_file",
   140  			`
   141  			server "test" {
   142  			}
   143  			definitions {
   144  				jwt_signing_profile "MyToken" {
   145  					signature_algorithm = "HS512"
   146  					key_file = "testdata/secret.txt"
   147  					ttl = "0"
   148  					claims = {
   149  					  iss = to_lower("The_Issuer")
   150  					  aud = to_upper("The_Audience")
   151  					}
   152  				}
   153  			}
   154  			`,
   155  			"MyToken",
   156  			`{"sub":"12345"}`,
   157  			"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.LCzGZMYxwLAra2tHNDFBCSKVzMdZeZGxhgGuVr0e9mHDXMqpyOiCBWN2JB-9aDUNPHobwxEWofPY8M9icL3YXQ",
   158  		},
   159  		{
   160  			"RS256 / PKCS#8 key",
   161  			`
   162  			server "test" {
   163  			}
   164  			definitions {
   165  				jwt_signing_profile "MyToken" {
   166  					signature_algorithm = "RS256"
   167  					key = <<EOF
   168  -----BEGIN PRIVATE KEY-----
   169  MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAMZJ36xJOyza465U
   170  kqm6akUBqaW3UJYsxMBk0BpsXvVSvQ/q7zfdGEAiNcNID9B5WK3O+q8Jmk21s4lV
   171  6BpESoswmq8U5SQV0s1E/d/1eNgUW+5ujT21yNfqgYREQoOosbYx9nZnwtXO4lzp
   172  LBkyBUWT/ret0A5yLdb27fM5IaPhAgMBAAECgYAzMjWvDQVbUnTAuRNZAUmY4ZIE
   173  uGz1KT/vkNfuSpbF7oPkDTuQh+RQAsUw03VJ1VrhHXS6JteRQt3FEsonpgQ8sYA3
   174  dSZ1ucEbFn58cApjc/EzmUv13sRPTwDS7/zq8H4ZnzDR6qyngQXOZcjktLrdxGj0
   175  YDmKWIakpa13YhQDZQJBAOdkclMdbbW+te8apeg/E6hAB+XG+cdgOA1BxEnGUSiQ
   176  WZIrjTmfsbb/82uxKddiZSwD/B2H1iwBhnfjHLqEoTcCQQDbYDDnwWfKlxEAoFOo
   177  RYe/3nuzMCbP1HqYsmobvbzdhZcvRUYM65Gp0LhCMyuLHc7xZsnEY1pUKu9gpW0o
   178  R8+nAkAYZbDgLpQou4j3QvoHOM9byBSNF6Oyb+S5ERwfIzffbQjkro7faoUUZHxH
   179  +JAO06MTNwBYJMBiN270KRAYYm+VAkAekJFfyN+YJ2IUD0J4oG9geTQM7DmMHVOT
   180  cNA92zq0Noew61ruM+gQm+cAOjbn5BhQI/0aoqkULwM5GPNTeGXjAkAITOarMByj
   181  YziZVz4/E9fRvcsdpETJdRF359oVdR5zFIUFKFwYdmX2fNYO0RnOB1ftxsbmdFSj
   182  X7CHMFB1ihCa
   183  -----END PRIVATE KEY-----
   184  					EOF
   185  					ttl = "0"
   186  					claims = {
   187  					  iss = to_lower("The_Issuer")
   188  					  aud = to_upper("The_Audience")
   189  					}
   190  				}
   191  			}
   192  			`,
   193  			"MyToken",
   194  			`{"sub":"12345"}`,
   195  			"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.oSS8rC1KonyZ-JZTZhkqZb5bN0_2Lrbl4J33nLgWroc5vDvmLW0KnX0RQfXy0OjX4uBBYTThActqqqM6vidaXmBfsQ77uB9narWeAptRnKqEPlY-onTHDmTMCz7vQ9wbLT7Aa6MYlhRqKX5adpPPbwBUuhm2I-yMF80nSmFpSk0",
   196  		},
   197  		{
   198  			"RS256 / PKCS#1 key_file",
   199  			`
   200  			server "test" {
   201  			}
   202  			definitions {
   203  				jwt_signing_profile "MyToken" {
   204  					signature_algorithm = "RS256"
   205  					key_file = "testdata/rsa_priv.pem"
   206  					ttl = "0"
   207  					claims = {
   208  					  iss = to_lower("The_Issuer")
   209  					  aud = to_upper("The_Audience")
   210  					}
   211  				}
   212  			}
   213  			`,
   214  			"MyToken",
   215  			`{"sub":"12345"}`,
   216  			"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.oSS8rC1KonyZ-JZTZhkqZb5bN0_2Lrbl4J33nLgWroc5vDvmLW0KnX0RQfXy0OjX4uBBYTThActqqqM6vidaXmBfsQ77uB9narWeAptRnKqEPlY-onTHDmTMCz7vQ9wbLT7Aa6MYlhRqKX5adpPPbwBUuhm2I-yMF80nSmFpSk0",
   217  		},
   218  		{
   219  			"RS384 / PKCS#1 key",
   220  			`
   221  			server "test" {
   222  			}
   223  			definitions {
   224  				jwt_signing_profile "MyToken" {
   225  					signature_algorithm = "RS384"
   226  					key = <<EOF
   227  -----BEGIN RSA PRIVATE KEY-----
   228  MIICWwIBAAKBgQDGSd+sSTss2uOuVJKpumpFAamlt1CWLMTAZNAabF71Ur0P6u83
   229  3RhAIjXDSA/QeVitzvqvCZpNtbOJVegaREqLMJqvFOUkFdLNRP3f9XjYFFvubo09
   230  tcjX6oGEREKDqLG2MfZ2Z8LVzuJc6SwZMgVFk/63rdAOci3W9u3zOSGj4QIDAQAB
   231  AoGAMzI1rw0FW1J0wLkTWQFJmOGSBLhs9Sk/75DX7kqWxe6D5A07kIfkUALFMNN1
   232  SdVa4R10uibXkULdxRLKJ6YEPLGAN3UmdbnBGxZ+fHAKY3PxM5lL9d7ET08A0u/8
   233  6vB+GZ8w0eqsp4EFzmXI5LS63cRo9GA5iliGpKWtd2IUA2UCQQDnZHJTHW21vrXv
   234  GqXoPxOoQAflxvnHYDgNQcRJxlEokFmSK405n7G2//NrsSnXYmUsA/wdh9YsAYZ3
   235  4xy6hKE3AkEA22Aw58FnypcRAKBTqEWHv957szAmz9R6mLJqG7283YWXL0VGDOuR
   236  qdC4QjMrix3O8WbJxGNaVCrvYKVtKEfPpwJAGGWw4C6UKLuI90L6BzjPW8gUjRej
   237  sm/kuREcHyM3320I5K6O32qFFGR8R/iQDtOjEzcAWCTAYjdu9CkQGGJvlQJAHpCR
   238  X8jfmCdiFA9CeKBvYHk0DOw5jB1Tk3DQPds6tDaHsOta7jPoEJvnADo25+QYUCP9
   239  GqKpFC8DORjzU3hl4wJACEzmqzAco2M4mVc+PxPX0b3LHaREyXURd+faFXUecxSF
   240  BShcGHZl9nzWDtEZzgdX7cbG5nRUo1+whzBQdYoQmg==
   241  -----END RSA PRIVATE KEY-----
   242  					EOF
   243  					ttl = "0"
   244  					claims = {
   245  					  iss = to_lower("The_Issuer")
   246  					  aud = to_upper("The_Audience")
   247  					}
   248  				}
   249  			}
   250  			`,
   251  			"MyToken",
   252  			`{"sub":"12345"}`,
   253  			"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.GaKMl5KxXXTlOUgfZy-Cs81jvVhp-2TjEZIg58qnjIbHH7P0YtIr8p4ikTHQhie7oXs5iwzQPdPqwJSUYHlia3t118mv1ie0IWjqmXhOcWsEODQYHfshIfKaqfIZaF7WFBZXfNdXX4g-8_aUrNPevZ_6vVhHq2844cjaKH-XGl4",
   254  		},
   255  		{
   256  			"RS384 / PKCS#8 key_file",
   257  			`
   258  			server "test" {
   259  			}
   260  			definitions {
   261  				jwt_signing_profile "MyToken" {
   262  					signature_algorithm = "RS384"
   263  					key_file = "testdata/rsa_pkcs8_priv.pem"
   264  					ttl = "0"
   265  					claims = {
   266  					  iss = to_lower("The_Issuer")
   267  					  aud = to_upper("The_Audience")
   268  					}
   269  				}
   270  			}
   271  			`,
   272  			"MyToken",
   273  			`{"sub":"12345"}`,
   274  			"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.GaKMl5KxXXTlOUgfZy-Cs81jvVhp-2TjEZIg58qnjIbHH7P0YtIr8p4ikTHQhie7oXs5iwzQPdPqwJSUYHlia3t118mv1ie0IWjqmXhOcWsEODQYHfshIfKaqfIZaF7WFBZXfNdXX4g-8_aUrNPevZ_6vVhHq2844cjaKH-XGl4",
   275  		},
   276  		{
   277  			"RS512 / PKCS#1 key",
   278  			`
   279  			server "test" {
   280  			}
   281  			definitions {
   282  				jwt_signing_profile "MyToken" {
   283  					signature_algorithm = "RS512"
   284  					key = <<EOF
   285  -----BEGIN RSA PRIVATE KEY-----
   286  MIICWwIBAAKBgQDGSd+sSTss2uOuVJKpumpFAamlt1CWLMTAZNAabF71Ur0P6u83
   287  3RhAIjXDSA/QeVitzvqvCZpNtbOJVegaREqLMJqvFOUkFdLNRP3f9XjYFFvubo09
   288  tcjX6oGEREKDqLG2MfZ2Z8LVzuJc6SwZMgVFk/63rdAOci3W9u3zOSGj4QIDAQAB
   289  AoGAMzI1rw0FW1J0wLkTWQFJmOGSBLhs9Sk/75DX7kqWxe6D5A07kIfkUALFMNN1
   290  SdVa4R10uibXkULdxRLKJ6YEPLGAN3UmdbnBGxZ+fHAKY3PxM5lL9d7ET08A0u/8
   291  6vB+GZ8w0eqsp4EFzmXI5LS63cRo9GA5iliGpKWtd2IUA2UCQQDnZHJTHW21vrXv
   292  GqXoPxOoQAflxvnHYDgNQcRJxlEokFmSK405n7G2//NrsSnXYmUsA/wdh9YsAYZ3
   293  4xy6hKE3AkEA22Aw58FnypcRAKBTqEWHv957szAmz9R6mLJqG7283YWXL0VGDOuR
   294  qdC4QjMrix3O8WbJxGNaVCrvYKVtKEfPpwJAGGWw4C6UKLuI90L6BzjPW8gUjRej
   295  sm/kuREcHyM3320I5K6O32qFFGR8R/iQDtOjEzcAWCTAYjdu9CkQGGJvlQJAHpCR
   296  X8jfmCdiFA9CeKBvYHk0DOw5jB1Tk3DQPds6tDaHsOta7jPoEJvnADo25+QYUCP9
   297  GqKpFC8DORjzU3hl4wJACEzmqzAco2M4mVc+PxPX0b3LHaREyXURd+faFXUecxSF
   298  BShcGHZl9nzWDtEZzgdX7cbG5nRUo1+whzBQdYoQmg==
   299  -----END RSA PRIVATE KEY-----
   300  					EOF
   301  					ttl = "0"
   302  					claims = {
   303  					  iss = to_lower("The_Issuer")
   304  					  aud = to_upper("The_Audience")
   305  					}
   306  				}
   307  			}
   308  			`,
   309  			"MyToken",
   310  			`{"sub":"12345"}`,
   311  			"eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.flU1adXUEaZuqkFwhcgJ8U3OXYOTC6RQCWw9rb7nkTNzt7XrU13EPtlxH5_7lpAvyBn4iyOCiJd19y1paupyeYbHEgUGsVXa4Iu1jQ8I7C41ejLNybdg7XpRzf3zt6tMC3W9Bp0TYRqrykTiQ0W4pg0sGJCV-e30dSDgkfuS_TM",
   312  		},
   313  		{
   314  			"RS512 / PKCS#8 key_file",
   315  			`
   316  			server "test" {
   317  			}
   318  			definitions {
   319  				jwt_signing_profile "MyToken" {
   320  					signature_algorithm = "RS512"
   321  					key_file = "testdata/rsa_pkcs8_priv.pem"
   322  					ttl = "0"
   323  					claims = {
   324  					  iss = to_lower("The_Issuer")
   325  					  aud = to_upper("The_Audience")
   326  					}
   327  				}
   328  			}
   329  			`,
   330  			"MyToken",
   331  			`{"sub":"12345"}`,
   332  			"eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.flU1adXUEaZuqkFwhcgJ8U3OXYOTC6RQCWw9rb7nkTNzt7XrU13EPtlxH5_7lpAvyBn4iyOCiJd19y1paupyeYbHEgUGsVXa4Iu1jQ8I7C41ejLNybdg7XpRzf3zt6tMC3W9Bp0TYRqrykTiQ0W4pg0sGJCV-e30dSDgkfuS_TM",
   333  		},
   334  		{
   335  			"ES256 / key_file",
   336  			`
   337  			server {}
   338  			definitions {
   339  				jwt_signing_profile "MyToken" {
   340  					signature_algorithm = "ES256"
   341  					key_file = "testdata/ecdsa_256_priv.pem"
   342  					ttl = "0"
   343  					claims = {
   344  					  iss = to_lower("The_Issuer")
   345  					  aud = to_upper("The_Audience")
   346  					}
   347  				}
   348  			}
   349  			`,
   350  			"MyToken",
   351  			`{"sub":"12345"}`,
   352  			"",
   353  		},
   354  		{
   355  			"ES384 / key w/ 'EC'",
   356  			`
   357  			server {}
   358  			definitions {
   359  				jwt_signing_profile "MyToken" {
   360  					signature_algorithm = "ES384"
   361  					key =<<-EOF
   362  						-----BEGIN EC PRIVATE KEY-----
   363  						ME4CAQAwEAYHKoZIzj0CAQYFK4EEACIENzA1AgEBBDBq1TvCPgzWTeRiI4Aj0CqN
   364  						MduYWGwc4gZcHCj07O1H36z5MGdd4pj0T2B/QrY7D20=
   365  						-----END EC PRIVATE KEY-----
   366  					EOF
   367  					ttl = "0"
   368  					claims = {
   369  					  iss = to_lower("The_Issuer")
   370  					  aud = to_upper("The_Audience")
   371  					}
   372  				}
   373  			}
   374  			`,
   375  			"MyToken",
   376  			`{"sub":"12345"}`,
   377  			"",
   378  		},
   379  		{
   380  			"jwt / HS256 / key",
   381  			`
   382  			server "test" {
   383  			}
   384  			definitions {
   385  				jwt "MySelfSignedToken" {
   386  					signature_algorithm = "HS256"
   387  					key = "$3cRe4"
   388  					signing_ttl = "0"
   389  					claims = {
   390  					  iss = to_lower("The_Issuer")
   391  					  aud = to_upper("The_Audience")
   392  					}
   393  				}
   394  			}
   395  			`,
   396  			"MySelfSignedToken",
   397  			`{"sub":"12345"}`,
   398  			"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.Hf-ZtIlsxR2bDOdAEMaDHaOBmfVWTQi9U68yV4YHW9w",
   399  		},
   400  		{
   401  			"jwt / HS256 / key_file",
   402  			`
   403  			server "test" {
   404  			}
   405  			definitions {
   406  				jwt "MySelfSignedToken" {
   407  					signature_algorithm = "HS256"
   408  					key_file = "testdata/secret.txt"
   409  					signing_ttl = "0"
   410  					claims = {
   411  					  iss = to_lower("The_Issuer")
   412  					  aud = to_upper("The_Audience")
   413  					}
   414  				}
   415  			}
   416  			`,
   417  			"MySelfSignedToken",
   418  			`{"sub":"12345"}`,
   419  			"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.Hf-ZtIlsxR2bDOdAEMaDHaOBmfVWTQi9U68yV4YHW9w",
   420  		},
   421  		{
   422  			"jwt / RS256 / PKCS#1 key",
   423  			`
   424  			server "test" {
   425  			}
   426  			definitions {
   427  				jwt "MySelfSignedToken" {
   428  					signature_algorithm = "RS256"
   429  					signing_key = <<EOF
   430  -----BEGIN RSA PRIVATE KEY-----
   431  MIICWwIBAAKBgQDGSd+sSTss2uOuVJKpumpFAamlt1CWLMTAZNAabF71Ur0P6u83
   432  3RhAIjXDSA/QeVitzvqvCZpNtbOJVegaREqLMJqvFOUkFdLNRP3f9XjYFFvubo09
   433  tcjX6oGEREKDqLG2MfZ2Z8LVzuJc6SwZMgVFk/63rdAOci3W9u3zOSGj4QIDAQAB
   434  AoGAMzI1rw0FW1J0wLkTWQFJmOGSBLhs9Sk/75DX7kqWxe6D5A07kIfkUALFMNN1
   435  SdVa4R10uibXkULdxRLKJ6YEPLGAN3UmdbnBGxZ+fHAKY3PxM5lL9d7ET08A0u/8
   436  6vB+GZ8w0eqsp4EFzmXI5LS63cRo9GA5iliGpKWtd2IUA2UCQQDnZHJTHW21vrXv
   437  GqXoPxOoQAflxvnHYDgNQcRJxlEokFmSK405n7G2//NrsSnXYmUsA/wdh9YsAYZ3
   438  4xy6hKE3AkEA22Aw58FnypcRAKBTqEWHv957szAmz9R6mLJqG7283YWXL0VGDOuR
   439  qdC4QjMrix3O8WbJxGNaVCrvYKVtKEfPpwJAGGWw4C6UKLuI90L6BzjPW8gUjRej
   440  sm/kuREcHyM3320I5K6O32qFFGR8R/iQDtOjEzcAWCTAYjdu9CkQGGJvlQJAHpCR
   441  X8jfmCdiFA9CeKBvYHk0DOw5jB1Tk3DQPds6tDaHsOta7jPoEJvnADo25+QYUCP9
   442  GqKpFC8DORjzU3hl4wJACEzmqzAco2M4mVc+PxPX0b3LHaREyXURd+faFXUecxSF
   443  BShcGHZl9nzWDtEZzgdX7cbG5nRUo1+whzBQdYoQmg==
   444  -----END RSA PRIVATE KEY-----
   445  					EOF
   446  					signing_ttl = "0"
   447  					claims = {
   448  					  iss = to_lower("The_Issuer")
   449  					  aud = to_upper("The_Audience")
   450  					}
   451  				}
   452  			}
   453  			`,
   454  			"MySelfSignedToken",
   455  			`{"sub":"12345"}`,
   456  			"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.oSS8rC1KonyZ-JZTZhkqZb5bN0_2Lrbl4J33nLgWroc5vDvmLW0KnX0RQfXy0OjX4uBBYTThActqqqM6vidaXmBfsQ77uB9narWeAptRnKqEPlY-onTHDmTMCz7vQ9wbLT7Aa6MYlhRqKX5adpPPbwBUuhm2I-yMF80nSmFpSk0",
   457  		},
   458  		{
   459  			"jwt / RS256 / PKCS#8 key_file",
   460  			`
   461  			server "test" {
   462  			}
   463  			definitions {
   464  				jwt "MySelfSignedToken" {
   465  					signature_algorithm = "RS256"
   466  					signing_key_file = "testdata/rsa_pkcs8_priv.pem"
   467  					signing_ttl = "0"
   468  					claims = {
   469  					  iss = to_lower("The_Issuer")
   470  					  aud = to_upper("The_Audience")
   471  					}
   472  				}
   473  			}
   474  			`,
   475  			"MySelfSignedToken",
   476  			`{"sub":"12345"}`,
   477  			"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJUSEVfQVVESUVOQ0UiLCJpc3MiOiJ0aGVfaXNzdWVyIiwic3ViIjoiMTIzNDUifQ.oSS8rC1KonyZ-JZTZhkqZb5bN0_2Lrbl4J33nLgWroc5vDvmLW0KnX0RQfXy0OjX4uBBYTThActqqqM6vidaXmBfsQ77uB9narWeAptRnKqEPlY-onTHDmTMCz7vQ9wbLT7Aa6MYlhRqKX5adpPPbwBUuhm2I-yMF80nSmFpSk0",
   478  		},
   479  		{
   480  			"jwt / ES512 / key",
   481  			`
   482  			server {}
   483  			definitions {
   484  				jwt "MySelfSignedToken" {
   485  					signature_algorithm = "ES512"
   486  					signing_key =<<-EOF
   487  						-----BEGIN PRIVATE KEY-----
   488  						MGACAQAwEAYHKoZIzj0CAQYFK4EEACMESTBHAgEBBEIBm9HVgPAxvAzYy5q6+DNM
   489  						4CQuGWaiBcwQSRSlLCVkfRclRf8BvTFRT8GATBsdSP/wl5xBFVeo/G7xu0t9wKK/
   490  						Cno=
   491  						-----END PRIVATE KEY-----
   492  					EOF
   493  					signing_ttl = "0"
   494  					claims = {
   495  					  iss = to_lower("The_Issuer")
   496  					  aud = to_upper("The_Audience")
   497  					}
   498  				}
   499  			}
   500  			`,
   501  			"MySelfSignedToken",
   502  			`{"sub":"12345"}`,
   503  			"",
   504  		},
   505  	}
   506  
   507  	for _, tt := range tests {
   508  		t.Run(tt.name, func(subT *testing.T) {
   509  			cf, err := configload.LoadBytes([]byte(tt.hcl), "couper.hcl")
   510  			if err != nil {
   511  				subT.Fatal(err)
   512  			}
   513  			claims, err := stdlib.JSONDecode(cty.StringVal(tt.claims))
   514  			if err != nil {
   515  				subT.Fatal(err)
   516  			}
   517  
   518  			hclContext := cf.Context.Value(request.ContextType).(*eval.Context).HCLContext()
   519  
   520  			token, err := hclContext.Functions[lib.FnJWTSign].Call([]cty.Value{cty.StringVal(tt.jspLabel), claims})
   521  			if err != nil {
   522  				subT.Fatal(err)
   523  			}
   524  			if tt.want != "" && token.AsString() != tt.want {
   525  				subT.Errorf("Expected %q, got: %#v", tt.want, token.AsString())
   526  			}
   527  		})
   528  	}
   529  }
   530  
   531  func TestJwtSignDynamic(t *testing.T) {
   532  	tests := []struct {
   533  		name     string
   534  		hcl      string
   535  		jspLabel string
   536  		headers  map[string]interface{}
   537  		claims   string
   538  		wantTTL  int64
   539  		wantMeth string
   540  	}{
   541  		{
   542  			"ttl 1h",
   543  			`
   544  			server "test" {
   545  			}
   546  			definitions {
   547  				jwt_signing_profile "MyToken" {
   548  					signature_algorithm = "HS256"
   549  					key = "$3cRe4"
   550  					ttl = "1h"
   551  					claims = {
   552  						x-method = request.method
   553  						x-status = backend_responses.default.status
   554  						exp = 1234567890
   555  					}
   556  				}
   557  			}
   558  			`,
   559  			"MyToken",
   560  			map[string]interface{}{"alg": "HS256", "typ": "JWT"},
   561  			`{"sub": "12345"}`,
   562  			3600,
   563  			http.MethodPost,
   564  		},
   565  		{
   566  			"ttl 60.6s",
   567  			`
   568  			server "test" {
   569  			}
   570  			definitions {
   571  				jwt_signing_profile "MyToken" {
   572  					signature_algorithm = "HS256"
   573  					key = "$3cRe4"
   574  					ttl = "60.6s"
   575  					claims = {
   576  						x-method = request.method
   577  						x-status = backend_responses.default.status
   578  					}
   579  				}
   580  			}
   581  			`,
   582  			"MyToken",
   583  			map[string]interface{}{"alg": "HS256", "typ": "JWT"},
   584  			`{"sub": "12345"}`,
   585  			60,
   586  			http.MethodPost,
   587  		},
   588  		{
   589  			"user-defined header",
   590  			`
   591  			server "test" {
   592  			}
   593  			definitions {
   594  				jwt_signing_profile "MyToken" {
   595  					signature_algorithm = "HS256"
   596  					key = "$3cRe4"
   597  					ttl = "1h"
   598  					headers = {
   599  						kid = "key-id"
   600  						foo = [request.method, backend_responses.default.status]
   601  						typ = "at+jwt"
   602  					}
   603  					claims = {
   604  						x-method = "GET"
   605  						x-status = 200
   606  					}
   607  				}
   608  			}
   609  			`,
   610  			"MyToken",
   611  			map[string]interface{}{"alg": "HS256", "typ": "at+jwt", "kid": "key-id", "foo": []interface{}{"GET", 200}},
   612  			`{"sub": "12345"}`,
   613  			3600,
   614  			http.MethodGet,
   615  		},
   616  	}
   617  
   618  	for _, tt := range tests {
   619  		t.Run(tt.name, func(subT *testing.T) {
   620  			helper := test.New(subT)
   621  
   622  			cf, err := configload.LoadBytes([]byte(tt.hcl), "couper.hcl")
   623  			helper.Must(err)
   624  
   625  			claims, err := stdlib.JSONDecode(cty.StringVal(tt.claims))
   626  			helper.Must(err)
   627  
   628  			req := httptest.NewRequest(tt.wantMeth, "http://1.2.3.4/", nil)
   629  			*req = *req.WithContext(context.Background())
   630  			beresp := &http.Response{
   631  				Request:    req,
   632  				StatusCode: http.StatusOK,
   633  			}
   634  
   635  			evalCtx, _, _, _ := cf.Context.Value(request.ContextType).(*eval.Context).
   636  				WithClientRequest(req).
   637  				WithBeresp(beresp, cty.NilVal)
   638  
   639  			now := time.Now().Unix()
   640  			token, err := evalCtx.HCLContext().Functions[lib.FnJWTSign].Call([]cty.Value{cty.StringVal(tt.jspLabel), claims})
   641  			helper.Must(err)
   642  
   643  			tokenParts := strings.Split(token.AsString(), ".")
   644  			if len(tokenParts) != 3 {
   645  				subT.Errorf("Needs 3 parts, got: %d", len(tokenParts))
   646  			}
   647  
   648  			joseHeader, err := base64.RawURLEncoding.DecodeString(tokenParts[0])
   649  			helper.Must(err)
   650  
   651  			var resultHeaders map[string]interface{}
   652  			err = json.Unmarshal(joseHeader, &resultHeaders)
   653  			helper.Must(err)
   654  
   655  			if fmt.Sprint(tt.headers) != fmt.Sprint(resultHeaders) {
   656  				subT.Errorf("Headers:\n\tWant: %#v\n\tGot:  %#v", tt.headers, resultHeaders)
   657  			}
   658  
   659  			body, err := base64.RawURLEncoding.DecodeString(tokenParts[1])
   660  			helper.Must(err)
   661  
   662  			var resultClaims map[string]interface{}
   663  			err = json.Unmarshal(body, &resultClaims)
   664  			helper.Must(err)
   665  
   666  			if resultClaims["exp"] == nil {
   667  				subT.Errorf("Expected exp claim, got: %s", body)
   668  			}
   669  			exp, _ := resultClaims["exp"].(float64)
   670  			if !fuzzyEqual(int64(exp)-now, tt.wantTTL, 1) {
   671  				subT.Error(string(body))
   672  				subT.Errorf("Expected %d, got: %d", tt.wantTTL, int64(exp)-now)
   673  			}
   674  			if resultClaims["x-method"] == nil {
   675  				subT.Errorf("Expected x-method claim, got: %s", body)
   676  			}
   677  			if resultClaims["x-method"] != tt.wantMeth {
   678  				subT.Errorf("Expected: %s, got: %s", tt.wantMeth, resultClaims["x-method"])
   679  			}
   680  
   681  			if resultClaims["x-status"] == nil {
   682  				subT.Errorf("Expected x-status claim, got: %s", body)
   683  			}
   684  			status, _ := resultClaims["x-status"].(float64)
   685  			if status != 200 {
   686  				subT.Errorf("Expected: %d, got: %d", http.StatusOK, int64(status))
   687  			}
   688  		})
   689  	}
   690  }
   691  
   692  func TestJwtSignConfigError(t *testing.T) {
   693  	tests := []struct {
   694  		name     string
   695  		hcl      string
   696  		jspLabel string
   697  		claims   string
   698  		wantErr  string
   699  	}{
   700  		{
   701  			"unsupported signature algorithm",
   702  			`
   703  			server "test" {
   704  			}
   705  			definitions {
   706  				jwt_signing_profile "MyToken" {
   707  					signature_algorithm = "invalid"
   708  					key = "$3cRe4"
   709  					ttl = "0"
   710  				}
   711  			}
   712  			`,
   713  			"MyToken",
   714  			`{"sub": "12345"}`,
   715  			"configuration error: MyToken: algorithm is not supported",
   716  		},
   717  		{
   718  			"missing signing key or key_file",
   719  			`
   720  			server "test" {
   721  			}
   722  			definitions {
   723  				jwt_signing_profile "MyToken" {
   724  					signature_algorithm = "RS256"
   725  					ttl = "0"
   726  				}
   727  			}
   728  			`,
   729  			"MyToken",
   730  			`{"sub": "12345"}`,
   731  			"configuration error: MyToken: jwt_signing_profile key: read error: required: configured attribute or file",
   732  		},
   733  		{
   734  			"Invalid ttl value",
   735  			`
   736  			server "test" {
   737  			}
   738  			definitions {
   739  				jwt_signing_profile "MyToken" {
   740  					signature_algorithm = "HS256"
   741  					key = "$3cRe4"
   742  					ttl = "invalid"
   743  				}
   744  			}
   745  			`,
   746  			"MyToken",
   747  			`{"sub": "12345"}`,
   748  			"configuration error: MyToken: time: invalid duration \"invalid\"",
   749  		},
   750  		{
   751  			"invalid PEM key format",
   752  			`
   753  			server "test" {
   754  			}
   755  			definitions {
   756  				jwt_signing_profile "MyToken" {
   757  					signature_algorithm = "RS256"
   758  					key = "invalid"
   759  					ttl = 0
   760  				}
   761  			}
   762  			`,
   763  			"MyToken",
   764  			`{"sub": "12345"}`,
   765  			"configuration error: MyToken: invalid key: Key must be a PEM encoded PKCS1 or PKCS8 key",
   766  		},
   767  		{
   768  			"jwt / missing signing key or key_file",
   769  			`
   770  			server "test" {
   771  			}
   772  			definitions {
   773  				jwt "MySelfSignedToken" {
   774  					signature_algorithm = "RS256"
   775  					key_file = "testdata/rsa_priv.pem"
   776  					signing_ttl = "0"
   777  				}
   778  			}
   779  			`,
   780  			"MySelfSignedToken",
   781  			`{"sub": "12345"}`,
   782  			"configuration error: MySelfSignedToken: jwt signing key: read error: required: configured attribute or file",
   783  		},
   784  		{
   785  			"jwt / Invalid signing_ttl value",
   786  			`
   787  			server "test" {
   788  			}
   789  			definitions {
   790  				jwt "MySelfSignedToken" {
   791  					signature_algorithm = "HS256"
   792  					signing_key = "$3cRe4"
   793  					signing_ttl = "invalid"
   794  				}
   795  			}
   796  			`,
   797  			"MySelfSignedToken",
   798  			`{"sub": "12345"}`,
   799  			"configuration error: MySelfSignedToken: time: invalid duration \"invalid\"",
   800  		},
   801  		{
   802  			"jwt / invalid PEM key format",
   803  			`
   804  			server "test" {
   805  			}
   806  			definitions {
   807  				jwt "MySelfSignedToken" {
   808  					signature_algorithm = "RS256"
   809  					signing_key = "invalid"
   810  					signing_ttl = "0"
   811  				}
   812  			}
   813  			`,
   814  			"MySelfSignedToken",
   815  			`{"sub": "12345"}`,
   816  			"configuration error: MySelfSignedToken: invalid key: Key must be a PEM encoded PKCS1 or PKCS8 key",
   817  		},
   818  		{
   819  			"user-defined alg header",
   820  			`
   821  			server "test" {
   822  			}
   823  			definitions {
   824  				jwt_signing_profile "MyToken" {
   825  					signature_algorithm = "HS256"
   826  					key = "$3cRe4"
   827  					ttl = "1h"
   828  					headers = {
   829  						alg = "none"
   830  					}
   831  				}
   832  			}
   833  			`,
   834  			"MyToken",
   835  			`{"sub": "12345"}`,
   836  			`configuration error: MyToken: "alg" cannot be set via "headers"`,
   837  		},
   838  	}
   839  
   840  	for _, tt := range tests {
   841  		t.Run(tt.name, func(subT *testing.T) {
   842  			_, err := configload.LoadBytes([]byte(tt.hcl), "couper.hcl")
   843  			if err == nil {
   844  				subT.Fatalf("expected an error '%s', got nothing", tt.wantErr)
   845  			}
   846  			logErr, _ := err.(errors.GoError)
   847  			if logErr == nil {
   848  				subT.Error("logErr should not be nil")
   849  			} else if logErr.LogError() != tt.wantErr {
   850  				subT.Errorf("\nwant:\t%s\ngot:\t%v", tt.wantErr, logErr.LogError())
   851  			}
   852  		})
   853  	}
   854  }
   855  
   856  func TestJwtSignError(t *testing.T) {
   857  	tests := []struct {
   858  		name     string
   859  		hcl      string
   860  		jspLabel string
   861  		claims   string
   862  		wantErr  string
   863  	}{
   864  		{
   865  			"missing signing profile definitions",
   866  			`
   867  			server {}
   868  			definitions {
   869  				jwt "MyToken" {
   870  					signature_algorithm = "HS256"
   871  					key = "$3cRe4"
   872  				}
   873  			}
   874  			`,
   875  			"MyToken",
   876  			`{"sub": "12345"}`,
   877  			`missing jwt_signing_profile or jwt (with signing_ttl) block with referenced label "MyToken"`,
   878  		},
   879  		{
   880  			"No profile for label",
   881  			`
   882  			server {}
   883  			definitions {
   884  				jwt_signing_profile "MyToken" {
   885  					signature_algorithm = "HS256"
   886  					key = "$3cRe4"
   887  					ttl = "0"
   888  				}
   889  			}
   890  			`,
   891  			"NoProfileForThisLabel",
   892  			`{"sub":"12345"}`,
   893  			`missing jwt_signing_profile or jwt (with signing_ttl) block with referenced label "NoProfileForThisLabel"`,
   894  		},
   895  		{
   896  			"argument claims no object",
   897  			`
   898  			server {}
   899  			definitions {
   900  				jwt_signing_profile "MyToken" {
   901  					signature_algorithm = "HS256"
   902  					key = "$3cRe4"
   903  					ttl = "0"
   904  				}
   905  			}
   906  			`,
   907  			"MyToken",
   908  			`"no object"`,
   909  			"json: cannot unmarshal string into Go value of type map[string]interface {}",
   910  		},
   911  		{
   912  			"jwt / No profile for label",
   913  			`
   914  			server {}
   915  			definitions {
   916  				jwt "MySelfSignedToken" {
   917  					signature_algorithm = "HS256"
   918  					key = "$3cRe4"
   919  					signing_ttl = "0"
   920  				}
   921  			}
   922  			`,
   923  			"NoProfileForThisLabel",
   924  			`{"sub": "12345"}`,
   925  			`missing jwt_signing_profile or jwt (with signing_ttl) block with referenced label "NoProfileForThisLabel"`,
   926  		},
   927  		{
   928  			"jwt / bad curve for algorithm",
   929  			`
   930  			server {}
   931  			definitions {
   932  				jwt_signing_profile "MyToken" {
   933  					signature_algorithm = "ES512"
   934  					key =<<-EOF
   935  						-----BEGIN EC PRIVATE KEY-----
   936  						MHcCAQEEIPhFjEWy9WowuN52bmIdbSD4gMKdBjFplPhU/jUf8GFyoAoGCCqGSM49
   937  						AwEHoUQDQgAEgPxsi3Y2J1FWrjXjacAWmbB+GIuzKPLrW5KikaxLtwuoDE61oaWM
   938  						M4H99mGPN7k4Bmamle8ne9Pr7rQhXuk8Iw==
   939  						-----END EC PRIVATE KEY-----
   940  					EOF
   941  					ttl = 0
   942  				}
   943  			}
   944  			`,
   945  			"MyToken",
   946  			`{"sub":"12345"}`,
   947  			"key is invalid",
   948  		},
   949  	}
   950  
   951  	for _, tt := range tests {
   952  		t.Run(tt.name, func(subT *testing.T) {
   953  			helper := test.New(subT)
   954  			cf, err := configload.LoadBytes([]byte(tt.hcl), "couper.hcl")
   955  			helper.Must(err)
   956  			claims, err := stdlib.JSONDecode(cty.StringVal(tt.claims))
   957  			helper.Must(err)
   958  
   959  			hclContext := cf.Context.Value(request.ContextType).(*eval.Context).HCLContext()
   960  
   961  			_, err = hclContext.Functions[lib.FnJWTSign].Call([]cty.Value{cty.StringVal(tt.jspLabel), claims})
   962  			if err == nil {
   963  				subT.Error("expected an error, got nothing")
   964  				return
   965  			}
   966  			if err.Error() != tt.wantErr {
   967  				subT.Errorf("\nWant:\t%q\nGot:\t%q", tt.wantErr, err.Error())
   968  			}
   969  		})
   970  	}
   971  }