ext_jwt: switch to new authlib (#87157)

This commit is contained in:
Charandas 2024-05-03 20:59:37 +01:00 committed by GitHub
parent babfa2beac
commit 0c59baf62d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 249 additions and 211 deletions

2
go.mod
View File

@ -87,7 +87,7 @@ require (
github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group github.com/gorilla/mux v1.8.1 // @grafana/grafana-backend-group
github.com/gorilla/websocket v1.5.0 // @grafana/grafana-app-platform-squad github.com/gorilla/websocket v1.5.0 // @grafana/grafana-app-platform-squad
github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36 // @grafana/alerting-squad-backend github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36 // @grafana/alerting-squad-backend
github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72 // @grafana/identity-access-team github.com/grafana/authlib v0.0.0-20240503035720-d1f918d6254a // @grafana/identity-access-team
github.com/grafana/codejen v0.0.3 // @grafana/dataviz-squad github.com/grafana/codejen v0.0.3 // @grafana/dataviz-squad
github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code github.com/grafana/cuetsy v0.1.11 // @grafana/grafana-as-code
github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics github.com/grafana/dataplane/examples v0.0.1 // @grafana/observability-metrics

4
go.sum
View File

@ -2150,8 +2150,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36 h1:v4aQ0cde8SCzNRrD2RczzmFolEkXWriSY9tKakAD0ng= github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36 h1:v4aQ0cde8SCzNRrD2RczzmFolEkXWriSY9tKakAD0ng=
github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36/go.mod h1:8nOsn7PWmttOmWiR7bvYIl3VLl+tIq72ZF+1y54w36M= github.com/grafana/alerting v0.0.0-20240424080142-bb4f4f429d36/go.mod h1:8nOsn7PWmttOmWiR7bvYIl3VLl+tIq72ZF+1y54w36M=
github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72 h1:lGEuhD/KhhN1OiPrvwQejl9Lg8MvaHdj3lHZNref4is= github.com/grafana/authlib v0.0.0-20240503035720-d1f918d6254a h1:9I4HHhjS634CWcsCS82wGvpkveypCzMe+2DMuzKoW5A=
github.com/grafana/authlib v0.0.0-20240328140636-a7388d0bac72/go.mod h1:86rRD5P6u2JPWtNWTMOlqlU+YMv2fUvVz/DomA6L7w4= github.com/grafana/authlib v0.0.0-20240503035720-d1f918d6254a/go.mod h1:86rRD5P6u2JPWtNWTMOlqlU+YMv2fUvVz/DomA6L7w4=
github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw= github.com/grafana/codejen v0.0.3 h1:tAWxoTUuhgmEqxJPOLtJoxlPBbMULFwKFOcRsPRPXDw=
github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s= github.com/grafana/codejen v0.0.3/go.mod h1:zmwwM/DRyQB7pfuBjTWII3CWtxcXh8LTwAYGfDfpR6s=
github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ= github.com/grafana/cue v0.0.0-20230926092038-971951014e3f h1:TmYAMnqg3d5KYEAaT6PtTguL2GjLfvr6wnAX8Azw6tQ=

View File

@ -1,4 +1,4 @@
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1 h1:tdpHgTbmbvEIARu+bixzmleMi14+3imnpoFXz+Qzjp4= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230721003620-2341cbb21958.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew=
buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew= buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.31.0-20230802163732-1c33ebd9ecfa.1/go.mod h1:xafc+XIsTxTy76GJQ1TKgvJWsSugFBqMaN27WhUblew=
buf.build/gen/go/grpc-ecosystem/grpc-gateway/bufbuild/connect-go v1.4.1-20221127060915-a1ecdc58eccd.1 h1:vp9EaPFSb75qe/793x58yE5fY1IJ/gdxb/kcDUzavtI= buf.build/gen/go/grpc-ecosystem/grpc-gateway/bufbuild/connect-go v1.4.1-20221127060915-a1ecdc58eccd.1 h1:vp9EaPFSb75qe/793x58yE5fY1IJ/gdxb/kcDUzavtI=
buf.build/gen/go/grpc-ecosystem/grpc-gateway/bufbuild/connect-go v1.4.1-20221127060915-a1ecdc58eccd.1/go.mod h1:YDq2B5X5BChU0lxAG5MxHpDb8mx1fv9OGtF2mwOe7hY= buf.build/gen/go/grpc-ecosystem/grpc-gateway/bufbuild/connect-go v1.4.1-20221127060915-a1ecdc58eccd.1/go.mod h1:YDq2B5X5BChU0lxAG5MxHpDb8mx1fv9OGtF2mwOe7hY=
@ -512,19 +512,15 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs= github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/bufbuild/protovalidate-go v0.2.1 h1:pJr07sYhliyfj/STAM7hU4J3FKpVeLVKvOBmOTN8j+s=
github.com/bufbuild/protovalidate-go v0.2.1/go.mod h1:e7XXDtlxj5vlEyAgsrxpzayp4cEMKCSSb8ZCkin+MVA= github.com/bufbuild/protovalidate-go v0.2.1/go.mod h1:e7XXDtlxj5vlEyAgsrxpzayp4cEMKCSSb8ZCkin+MVA=
github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw= github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw=
github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0=
github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/casbin/casbin/v2 v2.37.0 h1:/poEwPSovi4bTOcP752/CsTQiRz2xycyVKFG7GUhbDw= github.com/casbin/casbin/v2 v2.37.0 h1:/poEwPSovi4bTOcP752/CsTQiRz2xycyVKFG7GUhbDw=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U= github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U= github.com/chromedp/cdproto v0.0.0-20220208224320-6efb837e6bc2/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U=
github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw= github.com/chromedp/chromedp v0.9.2 h1:dKtNz4kApb06KuSXoTQIyUC2TrA0fhGDwNZf3bcgfKw=
@ -722,8 +718,7 @@ github.com/grafana/e2e v0.1.1-0.20221018202458-cffd2bb71c7b h1:Ha+kSIoTutf4ytlVw
github.com/grafana/e2e v0.1.1-0.20221018202458-cffd2bb71c7b/go.mod h1:3UsooRp7yW5/NJQBlXcTsAHOoykEhNUYXkQ3r6ehEEY= github.com/grafana/e2e v0.1.1-0.20221018202458-cffd2bb71c7b/go.mod h1:3UsooRp7yW5/NJQBlXcTsAHOoykEhNUYXkQ3r6ehEEY=
github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 h1:/of8Z8taCPftShATouOrBVy6GaTTjgQd/VfNiZp/VXQ= github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 h1:/of8Z8taCPftShATouOrBVy6GaTTjgQd/VfNiZp/VXQ=
github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU=
github.com/grafana/grafana-aws-sdk v0.25.1 h1:waJERJueqB1GldclAh5+tBcY9Ju9AOO9xYSJEnOAuf4= github.com/grafana/grafana-azure-sdk-go/v2 v2.0.2 h1:CWT7mOBPUht9n7F/NiBQnEM05pFmCP3Z8CZPGCVC1tM=
github.com/grafana/grafana-aws-sdk v0.25.1/go.mod h1:3zghFF6edrxn0d6k6X9HpGZXDH+VfA+MwD2Pc/9X0ec=
github.com/grafana/grafana-azure-sdk-go/v2 v2.0.2/go.mod h1:s8GLONgVh/svnSsO0Eo+OgXc/RZqozI5/0n+pNm3MEE= github.com/grafana/grafana-azure-sdk-go/v2 v2.0.2/go.mod h1:s8GLONgVh/svnSsO0Eo+OgXc/RZqozI5/0n+pNm3MEE=
github.com/grafana/grafana-plugin-sdk-go v0.212.0/go.mod h1:qsI4ktDf0lig74u8SLPJf9zRdVxWV/W4Wi+Ox6gifgs= github.com/grafana/grafana-plugin-sdk-go v0.212.0/go.mod h1:qsI4ktDf0lig74u8SLPJf9zRdVxWV/W4Wi+Ox6gifgs=
github.com/grafana/grafana-plugin-sdk-go v0.215.0/go.mod h1:nBsh3jRItKQUXDF2BQkiQCPxqrsSQeb+7hiFyJTO1RE= github.com/grafana/grafana-plugin-sdk-go v0.215.0/go.mod h1:nBsh3jRItKQUXDF2BQkiQCPxqrsSQeb+7hiFyJTO1RE=
@ -732,22 +727,17 @@ github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240426134450-5fe9f7b9dfd4
github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240426134450-5fe9f7b9dfd4/go.mod h1:UBDIuvdUGUI5fMDHDAl6yAVpFhfwl5ojMaw1N68775w= github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240426134450-5fe9f7b9dfd4/go.mod h1:UBDIuvdUGUI5fMDHDAl6yAVpFhfwl5ojMaw1N68775w=
github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240430073540-ce4d126ae8b8 h1:pyWJN79uW8QHZiQRasHGLCEkXSr3k6HCjdr0J2jZ3rU= github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240430073540-ce4d126ae8b8 h1:pyWJN79uW8QHZiQRasHGLCEkXSr3k6HCjdr0J2jZ3rU=
github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240430073540-ce4d126ae8b8/go.mod h1:u4K9vVN6eU86loO68977eTXGypC4brUCnk4sfDzutZU= github.com/grafana/grafana-plugin-sdk-go v0.227.1-0.20240430073540-ce4d126ae8b8/go.mod h1:u4K9vVN6eU86loO68977eTXGypC4brUCnk4sfDzutZU=
github.com/grafana/grafana-plugin-sdk-go v0.228.0 h1:LlPqyB+RZTtDy8RVYD7iQVJW5A0gMoGSI/+Ykz8HebQ=
github.com/grafana/grafana-plugin-sdk-go v0.228.0/go.mod h1:u4K9vVN6eU86loO68977eTXGypC4brUCnk4sfDzutZU=
github.com/grafana/grafana/pkg/promlib v0.0.3/go.mod h1:3El4NlsfALz8QQCbEGHGFvJUG+538QLMuALRhZ3pcoo= github.com/grafana/grafana/pkg/promlib v0.0.3/go.mod h1:3El4NlsfALz8QQCbEGHGFvJUG+538QLMuALRhZ3pcoo=
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y= github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU=
github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw=
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
github.com/hamba/avro/v2 v2.17.2 h1:6PKpEWzJfNnvBgn7m2/8WYaDOUASxfDU+Jyb4ojDgFY=
github.com/hamba/avro/v2 v2.17.2/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo= github.com/hamba/avro/v2 v2.17.2/go.mod h1:Q9YK+qxAhtVrNqOhwlZTATLgLA8qxG2vtvkhK8fJ7Jo=
github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc=
github.com/hanwen/go-fuse/v2 v2.1.0 h1:+32ffteETaLYClUj0a3aHjZ1hOPxxaNEHiZiujuDaek=
github.com/hashicorp/consul/sdk v0.15.0 h1:2qK9nDrr4tiJKRoxPGhm6B7xJjLVIQqkjiab2M4aKjU=
github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v0.16.1 h1:IVQwpTGNRRIHafnTs2dQLIk4ENtneRIEEJWOVDqz99o= github.com/hashicorp/go-hclog v0.16.1 h1:IVQwpTGNRRIHafnTs2dQLIk4ENtneRIEEJWOVDqz99o=
github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= github.com/hashicorp/go-hclog v0.16.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE=
github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ= github.com/hashicorp/mdns v1.0.4 h1:sY0CMhFmjIPDMlTB+HfymFHCaYLhgifZ0QhjaYKD/UQ=
@ -801,11 +791,6 @@ github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA
github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o=
github.com/jsternberg/zap-logfmt v1.2.0 h1:1v+PK4/B48cy8cfQbxL4FmmNZrjnIMr2BsnyEmXqv2o= github.com/jsternberg/zap-logfmt v1.2.0 h1:1v+PK4/B48cy8cfQbxL4FmmNZrjnIMr2BsnyEmXqv2o=
github.com/jsternberg/zap-logfmt v1.2.0/go.mod h1:kz+1CUmCutPWABnNkOu9hOHKdT2q3TDYCcsFy9hpqb0= github.com/jsternberg/zap-logfmt v1.2.0/go.mod h1:kz+1CUmCutPWABnNkOu9hOHKdT2q3TDYCcsFy9hpqb0=
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d h1:c93kUJDtVAXFEhsCh5jSxyOJmFHuzcihnslQiX8Urwo=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA=
github.com/karrick/godirwalk v1.10.3 h1:lOpSw2vJP0y5eLBW906QwKsUK/fe/QDyoqM5rnnuPDY=
github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4= github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4=
github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I=
github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk= github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk=
@ -820,13 +805,9 @@ github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY
github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4=
github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA=
github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw=
github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8=
github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY=
github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/knadh/koanf v1.5.0 h1:q2TSd/3Pyc/5yP9ldIrSdIz26MCcyNQzW0pEAugLPNs=
github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs= github.com/knadh/koanf v1.5.0/go.mod h1:Hgyjp4y8v44hpZtPzs7JZfRAW5AhN7KfZcwv1RYggDs=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
@ -1004,10 +985,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5I
github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ= github.com/sony/gobreaker v0.4.1 h1:oMnRNZXX5j85zso6xCPRNPtmAycat+WcoKbklScLDgQ=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw=
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
@ -1021,13 +999,9 @@ github.com/tdewolff/minify/v2 v2.12.9 h1:dvn5MtmuQ/DFMwqf5j8QhEVpPX6fi3WGImhv8RU
github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU= github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU=
github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvFMA= github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvFMA=
github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM= github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM=
github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
@ -1037,7 +1011,6 @@ github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYm
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o= github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o=
github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
@ -1149,7 +1122,6 @@ go.uber.org/mock v0.2.0/go.mod h1:J0y0rp9L3xiff1+ZBfKxlC1fz2+aO16tw0tsDOixfuM=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc=
golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
@ -1243,14 +1215,9 @@ gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg=
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/telebot.v3 v3.2.1 h1:3I4LohaAyJBiivGmkfB+CiVu7QFOWkuZ4+KHgO/G3rs=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o=
k8s.io/component-base v0.0.0-20240417101527-62c04b35eff6 h1:WN8Lymy+dCTDHgn4vhUSNIB6U+0sDiv/c9Zdr0UeAnI= k8s.io/component-base v0.0.0-20240417101527-62c04b35eff6 h1:WN8Lymy+dCTDHgn4vhUSNIB6U+0sDiv/c9Zdr0UeAnI=
k8s.io/component-base v0.0.0-20240417101527-62c04b35eff6/go.mod h1:l0ukbPS0lwFxOzSq5ZqjutzF+5IL2TLp495PswRPSZk= k8s.io/component-base v0.0.0-20240417101527-62c04b35eff6/go.mod h1:l0ukbPS0lwFxOzSq5ZqjutzF+5IL2TLp495PswRPSZk=
k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks=
k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo=
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8=
k8s.io/kms v0.29.0/go.mod h1:mB0f9HLxRXeXUfHfn1A7rpwOlzXI1gIWu86z6buNoYA= k8s.io/kms v0.29.0/go.mod h1:mB0f9HLxRXeXUfHfn1A7rpwOlzXI1gIWu86z6buNoYA=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=

View File

@ -4,15 +4,13 @@ import (
"context" "context"
"fmt" "fmt"
"net/http" "net/http"
"slices"
"strconv" "strconv"
"strings" "strings"
"github.com/go-jose/go-jose/v3/jwt" "github.com/go-jose/go-jose/v3/jwt"
authlib "github.com/grafana/authlib/authn" authlib "github.com/grafana/authlib/authn"
"github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
"github.com/grafana/grafana/pkg/services/authn" "github.com/grafana/grafana/pkg/services/authn"
"github.com/grafana/grafana/pkg/services/login" "github.com/grafana/grafana/pkg/services/login"
"github.com/grafana/grafana/pkg/services/signingkeys" "github.com/grafana/grafana/pkg/services/signingkeys"
@ -22,10 +20,6 @@ import (
var _ authn.Client = new(ExtendedJWT) var _ authn.Client = new(ExtendedJWT)
var (
acceptedSigningMethods = []string{"RS256", "ES256"}
)
const ( const (
rfc9068ShortMediaType = "at+jwt" rfc9068ShortMediaType = "at+jwt"
extJWTAuthenticationHeaderName = "X-Access-Token" extJWTAuthenticationHeaderName = "X-Access-Token"
@ -34,7 +28,14 @@ const (
func ProvideExtendedJWT(userService user.Service, cfg *setting.Cfg, func ProvideExtendedJWT(userService user.Service, cfg *setting.Cfg,
signingKeys signingkeys.Service) *ExtendedJWT { signingKeys signingkeys.Service) *ExtendedJWT {
verifier := authlib.NewVerifier[ExtendedJWTClaims](authlib.IDVerifierConfig{ verifier := authlib.NewAccessTokenVerifier(authlib.VerifierConfig{
SigningKeysURL: cfg.ExtJWTAuth.JWKSUrl,
AllowedAudiences: []string{
cfg.ExtJWTAuth.ExpectAudience,
},
})
idTokenVerifier := authlib.NewIDTokenVerifier(authlib.VerifierConfig{
SigningKeysURL: cfg.ExtJWTAuth.JWKSUrl, SigningKeysURL: cfg.ExtJWTAuth.JWKSUrl,
AllowedAudiences: []string{ AllowedAudiences: []string{
cfg.ExtJWTAuth.ExpectAudience, cfg.ExtJWTAuth.ExpectAudience,
@ -46,7 +47,10 @@ func ProvideExtendedJWT(userService user.Service, cfg *setting.Cfg,
log: log.New(authn.ClientExtendedJWT), log: log.New(authn.ClientExtendedJWT),
userService: userService, userService: userService,
signingKeys: signingKeys, signingKeys: signingKeys,
verifier: verifier, accessTokenVerifier: verifier,
namespaceMapper: request.GetNamespaceMapper(cfg),
idTokenVerifier: idTokenVerifier,
} }
} }
@ -55,31 +59,23 @@ type ExtendedJWT struct {
log log.Logger log log.Logger
userService user.Service userService user.Service
signingKeys signingkeys.Service signingKeys signingkeys.Service
verifier authlib.Verifier[ExtendedJWTClaims] accessTokenVerifier authlib.Verifier[authlib.AccessTokenClaims]
} idTokenVerifier authlib.Verifier[authlib.IDTokenClaims]
namespaceMapper request.NamespaceMapper
type ExtendedJWTClaims struct {
jwt.Claims
// Access policy scopes
Scopes []string `json:"scopes"`
// Grafana roles
Permissions []string `json:"permissions"`
// On-behalf-of user
DelegatedPermissions []string `json:"delegatedPermissions"`
} }
func (s *ExtendedJWT) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identity, error) { func (s *ExtendedJWT) Authenticate(ctx context.Context, r *authn.Request) (*authn.Identity, error) {
jwtToken := s.retrieveAuthenticationToken(r.HTTPRequest) jwtToken := s.retrieveAuthenticationToken(r.HTTPRequest)
claims, err := s.verifyRFC9068Token(ctx, jwtToken, rfc9068ShortMediaType) claims, err := s.accessTokenVerifier.Verify(ctx, jwtToken)
if err != nil { if err != nil {
s.log.Error("Failed to verify JWT", "error", err) s.log.Error("Failed to verify access token", "error", err)
return nil, errJWTInvalid.Errorf("Failed to verify JWT: %w", err) return nil, errJWTInvalid.Errorf("Failed to verify access token: %w", err)
} }
idToken := s.retrieveAuthorizationToken(r.HTTPRequest) idToken := s.retrieveAuthorizationToken(r.HTTPRequest)
if idToken != "" { if idToken != "" {
idTokenClaims, err := s.verifyRFC9068Token(ctx, idToken, "jwt") idTokenClaims, err := s.idTokenVerifier.Verify(ctx, idToken)
if err != nil { if err != nil {
s.log.Error("Failed to verify id token", "error", err) s.log.Error("Failed to verify id token", "error", err)
return nil, errJWTInvalid.Errorf("Failed to verify id token: %w", err) return nil, errJWTInvalid.Errorf("Failed to verify id token: %w", err)
@ -95,8 +91,18 @@ func (s *ExtendedJWT) IsEnabled() bool {
return s.cfg.ExtJWTAuth.Enabled return s.cfg.ExtJWTAuth.Enabled
} }
func (s *ExtendedJWT) authenticateAsUser(idTokenClaims, func (s *ExtendedJWT) authenticateAsUser(idTokenClaims *authlib.Claims[authlib.IDTokenClaims],
accessTokenClaims *ExtendedJWTClaims) (*authn.Identity, error) { accessTokenClaims *authlib.Claims[authlib.AccessTokenClaims]) (*authn.Identity, error) {
// compare the incoming namespace claim against what namespaceMapper returns
if allowedNamespace := s.namespaceMapper(s.getDefaultOrgID()); idTokenClaims.Rest.Namespace != allowedNamespace {
return nil, errJWTDisallowedNamespaceClaim
}
// since id token claims can never have a wildcard ("*") namespace claim, the below comparison effectively
// disallows wildcard claims in access tokens here in Grafana (they are only meant for service layer)
if accessTokenClaims.Rest.Namespace != idTokenClaims.Rest.Namespace {
return nil, errJWTMismatchedNamespaceClaims.Errorf("id token namespace: %s, access token namespace: %s", idTokenClaims.Rest.Namespace, accessTokenClaims.Rest.Namespace)
}
// Only allow access policies to impersonate // Only allow access policies to impersonate
if !strings.HasPrefix(accessTokenClaims.Subject, fmt.Sprintf("%s:", authn.NamespaceAccessPolicy)) { if !strings.HasPrefix(accessTokenClaims.Subject, fmt.Sprintf("%s:", authn.NamespaceAccessPolicy)) {
s.log.Error("Invalid subject", "subject", accessTokenClaims.Subject) s.log.Error("Invalid subject", "subject", accessTokenClaims.Subject)
@ -122,18 +128,23 @@ func (s *ExtendedJWT) authenticateAsUser(idTokenClaims,
ClientParams: authn.ClientParams{ ClientParams: authn.ClientParams{
SyncPermissions: true, SyncPermissions: true,
FetchPermissionsParams: authn.FetchPermissionsParams{ FetchPermissionsParams: authn.FetchPermissionsParams{
ActionsLookup: accessTokenClaims.DelegatedPermissions, ActionsLookup: accessTokenClaims.Rest.DelegatedPermissions,
}, },
FetchSyncedUser: true, FetchSyncedUser: true,
}}, nil }}, nil
} }
func (s *ExtendedJWT) authenticateAsService(claims *ExtendedJWTClaims) (*authn.Identity, error) { func (s *ExtendedJWT) authenticateAsService(claims *authlib.Claims[authlib.AccessTokenClaims]) (*authn.Identity, error) {
if !strings.HasPrefix(claims.Subject, fmt.Sprintf("%s:", authn.NamespaceAccessPolicy)) { if !strings.HasPrefix(claims.Subject, fmt.Sprintf("%s:", authn.NamespaceAccessPolicy)) {
s.log.Error("Invalid subject", "subject", claims.Subject) s.log.Error("Invalid subject", "subject", claims.Subject)
return nil, errJWTInvalid.Errorf("Failed to parse sub: %s", "invalid subject format") return nil, errJWTInvalid.Errorf("Failed to parse sub: %s", "invalid subject format")
} }
// same as asUser, disallows wildcard claims in access tokens here in Grafana (they are only meant for service layer)
if allowedNamespace := s.namespaceMapper(s.getDefaultOrgID()); claims.Rest.Namespace != allowedNamespace {
return nil, errJWTDisallowedNamespaceClaim
}
id, err := authn.ParseNamespaceID(claims.Subject) id, err := authn.ParseNamespaceID(claims.Subject)
if err != nil { if err != nil {
return nil, err return nil, err
@ -147,7 +158,7 @@ func (s *ExtendedJWT) authenticateAsService(claims *ExtendedJWTClaims) (*authn.I
ClientParams: authn.ClientParams{ ClientParams: authn.ClientParams{
SyncPermissions: true, SyncPermissions: true,
FetchPermissionsParams: authn.FetchPermissionsParams{ FetchPermissionsParams: authn.FetchPermissionsParams{
Roles: claims.Permissions, Roles: claims.Rest.Permissions,
}, },
FetchSyncedUser: false, FetchSyncedUser: false,
}, },
@ -202,59 +213,6 @@ func (s *ExtendedJWT) retrieveAuthorizationToken(httpRequest *http.Request) stri
return strings.TrimPrefix(jwtToken, "Bearer ") return strings.TrimPrefix(jwtToken, "Bearer ")
} }
// verifyRFC9068Token verifies the token against the RFC 9068 specification.
func (s *ExtendedJWT) verifyRFC9068Token(ctx context.Context, rawToken string, typ string) (*ExtendedJWTClaims, error) {
parsedToken, err := jwt.ParseSigned(rawToken)
if err != nil {
return nil, fmt.Errorf("failed to parse JWT: %w", err)
}
if len(parsedToken.Headers) != 1 {
return nil, fmt.Errorf("only one header supported, got %d", len(parsedToken.Headers))
}
parsedHeader := parsedToken.Headers[0]
typeHeader := parsedHeader.ExtraHeaders["typ"]
if typeHeader == nil {
return nil, fmt.Errorf("missing 'typ' field from the header")
}
jwtType := strings.ToLower(typeHeader.(string))
if !strings.EqualFold(jwtType, typ) {
return nil, fmt.Errorf("invalid JWT type: %s", jwtType)
}
if !slices.Contains(acceptedSigningMethods, parsedHeader.Algorithm) {
return nil, fmt.Errorf("invalid algorithm: %s. Accepted algorithms: %s",
parsedHeader.Algorithm, strings.Join(acceptedSigningMethods, ", "))
}
keyID := parsedHeader.KeyID
if keyID == "" {
return nil, fmt.Errorf("missing 'kid' field from the header")
}
claims, err := s.verifier.Verify(ctx, rawToken)
if err != nil {
return nil, fmt.Errorf("failed to verify JWT: %w", err)
}
if claims.Expiry == nil {
return nil, fmt.Errorf("missing 'exp' claim")
}
if claims.Subject == "" {
return nil, fmt.Errorf("missing 'sub' claim")
}
if claims.IssuedAt == nil {
return nil, fmt.Errorf("missing 'iat' claim")
}
return &claims.Rest, nil
}
func (s *ExtendedJWT) getDefaultOrgID() int64 { func (s *ExtendedJWT) getDefaultOrgID() int64 {
orgID := int64(1) orgID := int64(1)
if s.cfg.AutoAssignOrg && s.cfg.AutoAssignOrgId > 0 { if s.cfg.AutoAssignOrg && s.cfg.AutoAssignOrgId > 0 {

View File

@ -28,9 +28,14 @@ import (
"github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/setting"
) )
type (
JWTAccessTokenClaims = authlib.Claims[authlib.AccessTokenClaims]
JWTIDTokenClaims = authlib.Claims[authlib.IDTokenClaims]
)
var ( var (
validPayload = ExtendedJWTClaims{ validPayload = JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Subject: "access-policy:this-uid", Subject: "access-policy:this-uid",
Audience: jwt.Audience{"http://localhost:3000"}, Audience: jwt.Audience{"http://localhost:3000"},
@ -38,12 +43,15 @@ var (
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Scopes: []string{"profile", "groups"}, Scopes: []string{"profile", "groups"},
DelegatedPermissions: []string{"dashboards:create", "folders:read", "datasources:explore", "datasources.insights:read"}, DelegatedPermissions: []string{"dashboards:create", "folders:read", "datasources:explore", "datasources.insights:read"},
Permissions: []string{"fixed:folders:reader"}, Permissions: []string{"fixed:folders:reader"},
Namespace: "default", // org ID of 1 is special and translates to default
},
} }
validIDPayload = ExtendedJWTClaims{ validIDPayload = JWTIDTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Subject: "user:2", Subject: "user:2",
Audience: jwt.Audience{"http://localhost:3000"}, Audience: jwt.Audience{"http://localhost:3000"},
@ -51,24 +59,61 @@ var (
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
}, },
Scopes: []string{"profile", "groups"}, Rest: authlib.IDTokenClaims{
AuthenticatedBy: "extended_jwt",
Namespace: "default", // org ID of 1 is special and translates to default
},
}
validPayloadWildcardNamespace = JWTAccessTokenClaims{
Claims: &jwt.Claims{
Issuer: "http://localhost:3000",
Subject: "access-policy:this-uid",
Audience: jwt.Audience{"http://localhost:3000"},
ID: "1234567890",
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
},
Rest: authlib.AccessTokenClaims{
Namespace: "*",
},
}
mismatchingNamespaceIDPayload = JWTIDTokenClaims{
Claims: &jwt.Claims{
Issuer: "http://localhost:3000",
Subject: "user:2",
Audience: jwt.Audience{"http://localhost:3000"},
ID: "1234567890",
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
},
Rest: authlib.IDTokenClaims{
AuthenticatedBy: "extended_jwt",
Namespace: "org-2",
},
} }
pk, _ = rsa.GenerateKey(rand.Reader, 4096) pk, _ = rsa.GenerateKey(rand.Reader, 4096)
) )
type mockVerifier struct { var _ authlib.Verifier[authlib.IDTokenClaims] = &mockIDVerifier{}
Claims []ExtendedJWTClaims
type mockIDVerifier struct {
Claims JWTIDTokenClaims
Error error Error error
counter int
} }
func (m *mockVerifier) Verify(ctx context.Context, token string) (*authlib.Claims[ExtendedJWTClaims], error) { func (m *mockIDVerifier) Verify(ctx context.Context, token string) (*JWTIDTokenClaims, error) {
m.counter++ return &m.Claims, m.Error
claims := m.Claims[m.counter-1] }
return &authlib.Claims[ExtendedJWTClaims]{
Claims: &claims.Claims, var _ authlib.Verifier[authlib.AccessTokenClaims] = &mockVerifier{}
Rest: claims,
}, m.Error type mockVerifier struct {
Claims JWTAccessTokenClaims
Error error
}
func (m *mockVerifier) Verify(ctx context.Context, token string) (*JWTAccessTokenClaims, error) {
return &m.Claims, m.Error
} }
func TestExtendedJWT_Test(t *testing.T) { func TestExtendedJWT_Test(t *testing.T) {
@ -93,13 +138,13 @@ func TestExtendedJWT_Test(t *testing.T) {
{ {
name: "should return true when Authorization header contains Bearer prefix", name: "should return true when Authorization header contains Bearer prefix",
cfg: nil, cfg: nil,
authHeaderFunc: func() string { return "Bearer " + generateToken(validPayload, pk, jose.RS256, "at+jwt") }, authHeaderFunc: func() string { return "Bearer " + generateToken(validPayload, pk, jose.RS256) },
want: true, want: true,
}, },
{ {
name: "should return true when Authorization header only contains the token", name: "should return true when Authorization header only contains the token",
cfg: nil, cfg: nil,
authHeaderFunc: func() string { return generateToken(validPayload, pk, jose.RS256, "at+jwt") }, authHeaderFunc: func() string { return generateToken(validPayload, pk, jose.RS256) },
want: true, want: true,
}, },
{ {
@ -124,7 +169,7 @@ func TestExtendedJWT_Test(t *testing.T) {
authHeaderFunc: func() string { authHeaderFunc: func() string {
payload := validPayload payload := validPayload
payload.Issuer = "http://unknown-issuer" payload.Issuer = "http://unknown-issuer"
return generateToken(payload, pk, jose.RS256, "at+jwt") return generateToken(payload, pk, jose.RS256)
}, },
want: false, want: false,
}, },
@ -152,8 +197,8 @@ func TestExtendedJWT_Test(t *testing.T) {
func TestExtendedJWT_Authenticate(t *testing.T) { func TestExtendedJWT_Authenticate(t *testing.T) {
type testCase struct { type testCase struct {
name string name string
payload ExtendedJWTClaims payload *JWTAccessTokenClaims
idPayload *ExtendedJWTClaims idPayload *JWTIDTokenClaims
orgID int64 orgID int64
want *authn.Identity want *authn.Identity
initTestEnv func(env *testEnv) initTestEnv func(env *testEnv)
@ -162,7 +207,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
testCases := []testCase{ testCases := []testCase{
{ {
name: "successful authentication as service", name: "successful authentication as service",
payload: validPayload, payload: &validPayload,
orgID: 1, orgID: 1,
want: &authn.Identity{OrgID: 1, OrgName: "", want: &authn.Identity{OrgID: 1, OrgName: "",
OrgRoles: map[int64]roletype.RoleType(nil), OrgRoles: map[int64]roletype.RoleType(nil),
@ -182,7 +227,7 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
}, },
{ {
name: "successful authentication as user", name: "successful authentication as user",
payload: validPayload, payload: &validPayload,
idPayload: &validIDPayload, idPayload: &validIDPayload,
orgID: 1, orgID: 1,
initTestEnv: func(env *testEnv) { initTestEnv: func(env *testEnv) {
@ -212,10 +257,44 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
Roles: []string(nil)}}, Permissions: map[int64]map[string][]string(nil), IDToken: ""}, Roles: []string(nil)}}, Permissions: map[int64]map[string][]string(nil), IDToken: ""},
wantErr: nil, wantErr: nil,
}, },
{
name: "fail authentication as user when access token namespace claim doesn't match id token namespace",
payload: &validPayload,
idPayload: &mismatchingNamespaceIDPayload,
orgID: 1,
initTestEnv: func(env *testEnv) {
env.userSvc.ExpectedSignedInUser = &user.SignedInUser{
UserID: 2,
OrgID: 1,
OrgRole: roletype.RoleAdmin,
Name: "John Doe",
Email: "johndoe@grafana.com",
Login: "johndoe",
}
},
wantErr: errJWTMismatchedNamespaceClaims.Errorf("id token namespace: %s, access token namespace: %s", mismatchingNamespaceIDPayload.Rest.Namespace, validPayload.Rest.Namespace),
},
{
name: "fail authentication as user when id token namespace claim doesn't match allowed namespace",
payload: &validPayloadWildcardNamespace,
idPayload: &validIDPayload,
orgID: 1,
initTestEnv: func(env *testEnv) {
env.userSvc.ExpectedSignedInUser = &user.SignedInUser{
UserID: 2,
OrgID: 1,
OrgRole: roletype.RoleAdmin,
Name: "John Doe",
Email: "johndoe@grafana.com",
Login: "johndoe",
}
},
wantErr: errJWTDisallowedNamespaceClaim,
},
{ {
name: "should return error when the subject is not an access-policy", name: "should return error when the subject is not an access-policy",
payload: ExtendedJWTClaims{ payload: &JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Subject: "user:2", Subject: "user:2",
Audience: jwt.Audience{"http://localhost:3000"}, Audience: jwt.Audience{"http://localhost:3000"},
@ -223,8 +302,10 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Permissions: []string{"fixed:folders:reader"}, Permissions: []string{"fixed:folders:reader"},
}, },
},
orgID: 1, orgID: 1,
want: nil, want: nil,
wantErr: errJWTInvalid.Errorf("Failed to parse sub: %s", "invalid subject format"), wantErr: errJWTInvalid.Errorf("Failed to parse sub: %s", "invalid subject format"),
@ -240,14 +321,15 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
validHTTPReq := &http.Request{ validHTTPReq := &http.Request{
Header: map[string][]string{ Header: map[string][]string{
"X-Access-Token": {generateToken(tc.payload, pk, jose.RS256, "at+jwt")}, "X-Access-Token": {generateToken(*tc.payload, pk, jose.RS256)},
}, },
} }
env.s.verifier = &mockVerifier{Claims: []ExtendedJWTClaims{tc.payload}} env.s.accessTokenVerifier = &mockVerifier{Claims: *tc.payload}
if tc.idPayload != nil { if tc.idPayload != nil {
env.s.verifier = &mockVerifier{Claims: []ExtendedJWTClaims{tc.payload, *tc.idPayload}} env.s.accessTokenVerifier = &mockVerifier{Claims: *tc.payload}
validHTTPReq.Header.Add(extJWTAuthorizationHeaderName, generateToken(*tc.idPayload, pk, jose.RS256, "jwt")) env.s.idTokenVerifier = &mockIDVerifier{Claims: *tc.idPayload}
validHTTPReq.Header.Add(extJWTAuthorizationHeaderName, generateIDToken(*tc.idPayload, pk, jose.RS256))
} }
id, err := env.s.Authenticate(context.Background(), &authn.Request{ id, err := env.s.Authenticate(context.Background(), &authn.Request{
@ -269,42 +351,47 @@ func TestExtendedJWT_Authenticate(t *testing.T) {
func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) { func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) {
type testCase struct { type testCase struct {
name string name string
payload ExtendedJWTClaims payload *JWTAccessTokenClaims
idPayload *JWTIDTokenClaims
alg jose.SignatureAlgorithm alg jose.SignatureAlgorithm
typ string generateWrongTyp bool
} }
testCases := []testCase{ testCases := []testCase{
{ {
name: "missing iss", name: "missing iss",
payload: ExtendedJWTClaims{ payload: &JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Subject: "access-policy:this-uid", Subject: "access-policy:this-uid",
Audience: jwt.Audience{"http://localhost:3000"}, Audience: jwt.Audience{"http://localhost:3000"},
ID: "1234567890", ID: "1234567890",
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Scopes: []string{"profile", "groups"}, Scopes: []string{"profile", "groups"},
}, },
}, },
},
{ {
name: "missing expiry", name: "missing expiry",
payload: ExtendedJWTClaims{ payload: &JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Subject: "access-policy:this-uid", Subject: "access-policy:this-uid",
Audience: jwt.Audience{"http://localhost:3000"}, Audience: jwt.Audience{"http://localhost:3000"},
ID: "1234567890", ID: "1234567890",
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Scopes: []string{"profile", "groups"}, Scopes: []string{"profile", "groups"},
}, },
}, },
},
{ {
name: "expired token", name: "expired token",
payload: ExtendedJWTClaims{ payload: &JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Subject: "access-policy:this-uid", Subject: "access-policy:this-uid",
Audience: jwt.Audience{"http://localhost:3000"}, Audience: jwt.Audience{"http://localhost:3000"},
@ -312,26 +399,30 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Scopes: []string{"profile", "groups"}, Scopes: []string{"profile", "groups"},
}, },
}, },
},
{ {
name: "missing aud", name: "missing aud",
payload: ExtendedJWTClaims{ payload: &JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Subject: "access-policy:this-uid", Subject: "access-policy:this-uid",
ID: "1234567890", ID: "1234567890",
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Scopes: []string{"profile", "groups"}, Scopes: []string{"profile", "groups"},
}, },
}, },
},
{ {
name: "wrong aud", name: "wrong aud",
payload: ExtendedJWTClaims{ payload: &JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Subject: "access-policy:this-uid", Subject: "access-policy:this-uid",
Audience: jwt.Audience{"http://some-other-host:3000"}, Audience: jwt.Audience{"http://some-other-host:3000"},
@ -339,54 +430,50 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Scopes: []string{"profile", "groups"}, Scopes: []string{"profile", "groups"},
}, },
}, },
},
{ {
name: "wrong typ", name: "wrong typ",
payload: ExtendedJWTClaims{ idPayload: &validIDPayload,
Claims: jwt.Claims{ generateWrongTyp: true,
Issuer: "http://localhost:3000",
Subject: "access-policy:this-uid",
Audience: jwt.Audience{"http://some-other-host:3000"},
ID: "1234567890",
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
},
Scopes: []string{"profile", "groups"},
},
typ: "jwt",
}, },
{ {
name: "missing sub", name: "missing sub",
payload: ExtendedJWTClaims{ payload: &JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Audience: jwt.Audience{"http://localhost:3000"}, Audience: jwt.Audience{"http://localhost:3000"},
ID: "1234567890", ID: "1234567890",
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Scopes: []string{"profile", "groups"}, Scopes: []string{"profile", "groups"},
}, },
}, },
},
{ {
name: "missing iat", name: "missing iat",
payload: ExtendedJWTClaims{ payload: &JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Subject: "access-policy:this-uid", Subject: "access-policy:this-uid",
Audience: jwt.Audience{"http://localhost:3000"}, Audience: jwt.Audience{"http://localhost:3000"},
ID: "1234567890", ID: "1234567890",
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Scopes: []string{"profile", "groups"}, Scopes: []string{"profile", "groups"},
}, },
}, },
},
{ {
name: "iat later than current time", name: "iat later than current time",
payload: ExtendedJWTClaims{ payload: &JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Subject: "access-policy:this-uid", Subject: "access-policy:this-uid",
Audience: jwt.Audience{"http://localhost:3000"}, Audience: jwt.Audience{"http://localhost:3000"},
@ -394,13 +481,15 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 2, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 2, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Scopes: []string{"profile", "groups"}, Scopes: []string{"profile", "groups"},
}, },
}, },
},
{ {
name: "unsupported alg", name: "unsupported alg",
payload: ExtendedJWTClaims{ payload: &JWTAccessTokenClaims{
Claims: jwt.Claims{ Claims: &jwt.Claims{
Issuer: "http://localhost:3000", Issuer: "http://localhost:3000",
Subject: "access-policy:this-uid", Subject: "access-policy:this-uid",
Audience: jwt.Audience{"http://localhost:3000"}, Audience: jwt.Audience{"http://localhost:3000"},
@ -408,8 +497,10 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) {
Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)), Expiry: jwt.NewNumericDate(time.Date(2023, 5, 3, 0, 0, 0, 0, time.UTC)),
IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)), IssuedAt: jwt.NewNumericDate(time.Date(2023, 5, 2, 0, 0, 0, 0, time.UTC)),
}, },
Rest: authlib.AccessTokenClaims{
Scopes: []string{"profile", "groups"}, Scopes: []string{"profile", "groups"},
}, },
},
alg: jose.RS384, alg: jose.RS384,
}, },
} }
@ -421,8 +512,14 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) {
if tc.alg == "" { if tc.alg == "" {
tc.alg = jose.RS256 tc.alg = jose.RS256
} }
tokenToTest := generateToken(tc.payload, pk, tc.alg, "at+jwt")
_, err := env.s.verifyRFC9068Token(context.Background(), tokenToTest, rfc9068ShortMediaType) var tokenToTest string
if tc.generateWrongTyp {
tokenToTest = generateIDToken(*tc.idPayload, pk, tc.alg)
} else {
tokenToTest = generateToken(*tc.payload, pk, tc.alg)
}
_, err := env.s.accessTokenVerifier.Verify(context.Background(), tokenToTest)
require.Error(t, err) require.Error(t, err)
}) })
} }
@ -431,6 +528,7 @@ func TestVerifyRFC9068TokenFailureScenarios(t *testing.T) {
func setupTestCtx(cfg *setting.Cfg) *testEnv { func setupTestCtx(cfg *setting.Cfg) *testEnv {
if cfg == nil { if cfg == nil {
cfg = &setting.Cfg{ cfg = &setting.Cfg{
// default org set up by the authenticator is 1
ExtJWTAuth: setting.ExtJWTSettings{ ExtJWTAuth: setting.ExtJWTSettings{
Enabled: true, Enabled: true,
ExpectIssuer: "http://localhost:3000", ExpectIssuer: "http://localhost:3000",
@ -459,10 +557,21 @@ type testEnv struct {
s *ExtendedJWT s *ExtendedJWT
} }
func generateToken(payload ExtendedJWTClaims, signingKey any, alg jose.SignatureAlgorithm, typ string) string { func generateToken(payload JWTAccessTokenClaims, signingKey any, alg jose.SignatureAlgorithm) string {
signer, _ := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, &jose.SignerOptions{ signer, _ := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, &jose.SignerOptions{
ExtraHeaders: map[jose.HeaderKey]any{ ExtraHeaders: map[jose.HeaderKey]any{
jose.HeaderType: typ, jose.HeaderType: authlib.TokenTypeAccess,
"kid": "default",
}})
result, _ := jwt.Signed(signer).Claims(payload).CompactSerialize()
return result
}
func generateIDToken(payload JWTIDTokenClaims, signingKey any, alg jose.SignatureAlgorithm) string {
signer, _ := jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: signingKey}, &jose.SignerOptions{
ExtraHeaders: map[jose.HeaderKey]any{
jose.HeaderType: authlib.TokenTypeID,
"kid": "default", "kid": "default",
}}) }})

View File

@ -27,6 +27,10 @@ var (
"jwt.missing_claim", errutil.WithPublicMessage("Missing mandatory claim in JWT")) "jwt.missing_claim", errutil.WithPublicMessage("Missing mandatory claim in JWT"))
errJWTInvalidRole = errutil.Forbidden( errJWTInvalidRole = errutil.Forbidden(
"jwt.invalid_role", errutil.WithPublicMessage("Invalid Role in claim")) "jwt.invalid_role", errutil.WithPublicMessage("Invalid Role in claim"))
errJWTMismatchedNamespaceClaims = errutil.Unauthorized(
"jwt.namespace_mismatch", errutil.WithPublicMessage("Namespace claims didn't match between id token and access token"))
errJWTDisallowedNamespaceClaim = errutil.Unauthorized(
"jwt.namespace_mismatch", errutil.WithPublicMessage("Namespace claim doesn't allow access to requested namespace"))
) )
func ProvideJWT(jwtService auth.JWTVerifierService, cfg *setting.Cfg) *JWT { func ProvideJWT(jwtService auth.JWTVerifierService, cfg *setting.Cfg) *JWT {