From c593003fb83591130ad68e71c550f680fa6d87d0 Mon Sep 17 00:00:00 2001 From: roland Date: Tue, 23 Mar 2021 09:59:00 +0100 Subject: [PATCH 1/7] Change model for persistent storage. --- .coverage | 2 +- setup.py | 2 +- src/oidcservice/__init__.py | 2 +- src/oidcservice/client_auth.py | 2 +- src/oidcservice/oauth2/access_token.py | 10 +- src/oidcservice/oauth2/authorization.py | 4 +- .../client_credentials/cc_access_token.py | 2 +- .../cc_refresh_access_token.py | 14 +- .../oauth2/refresh_access_token.py | 13 +- src/oidcservice/oidc/access_token.py | 22 +- src/oidcservice/oidc/add_on/pkce.py | 4 +- src/oidcservice/oidc/authorization.py | 23 +- src/oidcservice/oidc/check_id.py | 2 +- src/oidcservice/oidc/check_session.py | 2 +- src/oidcservice/oidc/end_session.py | 4 +- src/oidcservice/oidc/userinfo.py | 7 +- src/oidcservice/service.py | 113 +++-- src/oidcservice/service_context.py | 87 ++-- src/oidcservice/state_interface.py | 38 +- tests/request123456.jwt | 2 +- tests/test_01_service_context.py | 10 +- tests/test_01_service_context_impexp.py | 282 +++++++++++ tests/test_07_service.py | 4 +- tests/test_07_service_impexp.py | 72 +++ tests/test_09_client_auth.py | 46 +- tests/test_10_oauth2_service.py | 29 +- tests/test_13_oic_service.py | 77 +-- tests/test_14_pkce.py | 14 +- tests/test_16_cc_oauth2_service.py | 108 ++++- tests/test_20_conversation.py | 67 +-- tests/test_21_pushed_auth.py | 2 +- tests/test_30_persistence.py | 444 ------------------ 32 files changed, 822 insertions(+), 688 deletions(-) create mode 100644 tests/test_01_service_context_impexp.py create mode 100644 tests/test_07_service_impexp.py delete mode 100644 tests/test_30_persistence.py diff --git a/.coverage b/.coverage index 8e92d5b..416b191 100644 --- a/.coverage +++ b/.coverage @@ -1 +1 @@ -!coverage.py: This is a private format, don't read it directly!{"lines":{"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_01_service_context.py":[1,2,4,5,6,8,11,28,34,40,51,80,81,91,106,124,147,213,233,247,13,14,15,16,18,19,20,21,22,23,25,29,30,31,35,36,37,42,43,44,46,47,48,84,85,86,87,89,92,93,94,95,96,97,98,99,102,103,107,108,109,110,111,112,113,114,115,118,120,121,122,125,126,127,128,129,130,131,132,133,134,135,136,139,141,142,143,144,145,148,149,150,152,153,154,155,156,157,158,160,162,163,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,196,197,198,201,71,72,74,75,203,77,206,210,214,215,216,217,218,219,220,223,224,225,226,227,228,229,231,236,238,239,241,242,245,248,251,253,255,256,257,258,259,260,261,264],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/__init__.py":[1,2,5,6,7,8,11,12,15,17,18,19,23,24,25,26,27,29,31,32,35,46,49,61,42,43,58],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/service_context.py":[4,5,6,7,9,10,11,12,13,16,17,21,22,23,26,27,28,31,32,33,39,40,41,44,45,46,49,50,51,54,59,60,61,62,63,64,65,69,81,87,89,154,157,179,202,227,244,265,268,274,90,91,92,94,97,99,101,104,105,106,107,108,109,110,111,113,115,117,122,269,272,124,125,126,127,129,132,133,134,135,136,138,139,140,141,142,144,149,150,151,152,118,119,120,146,266,170,173,174,175,234,235,236,237,238,239,240,242,251,252,253,254,261,263,187,188,189,192,194,197,199,200,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,130,275,177,190,191,195,270],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_03_util.py":[1,2,4,6,7,9,12,30,46,62,78,13,14,15,16,18,19,20,21,23,24,25,26,33,34,37,38,40,42,43,49,50,53,54,56,58,59,65,66,69,70,72,74,75,79,80,81,82,84,85,86,87,89,90,91,92],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/util.py":[1,2,3,4,6,7,9,11,13,14,15,18,44,67,74,89,28,29,30,31,32,35,36,37,54,55,57,58,33,91,76,82,83,86,92,93,41,39],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_07_service.py":[1,2,5,6,7,10,12,13,14,18,19,25,26,29,30,38,44,50,58,68,69,74,32,33,34,35,36,39,40,41,42,45,46,47,48,51,52,53,54,55,56,59,60,61,62,63,64,65,71,72,75,76,77,78],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/service.py":[1,2,3,5,6,7,9,10,11,12,13,16,18,20,22,24,27,28,29,30,31,32,33,34,35,36,37,38,39,42,66,107,123,143,158,167,204,226,242,253,275,285,355,373,384,404,413,435,507,523,539,43,45,46,50,51,52,60,63,64,177,183,184,133,115,116,117,118,121,134,135,141,188,194,75,83,84,87,88,89,90,91,94,95,96,97,98,99,100,101,102,103,105,199,201,151,153,156,85,306,307,308,309,282,310,311,313,237,240,314,315,317,318,323,324,325,263,265,273,328,329,330,331,248,249,333,336,348,351,364,365,367,368,371,551,552,553,554,555,556,558,559,560,563,564,48,568,570,573,574,578,178,136,137,190,191,92,455,458,460,471,472,474,478,480,414,415,416,433,482,485,488,489,490,493,499,501,505,382,154,266,267,216,217,219,220,221,222,268,269,338,339,345,346,349,238,53,54,57,58,270,271,319,456,494,495,496,497,475,405,406,407,408,409,410,411,476,340,343,392,393,394,395,398,402,566,517,520,399,165,251],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/client_auth.py":[1,2,3,4,6,7,8,9,10,11,13,15,17,20,21,24,25,29,49,53,55,59,68,79,81,92,100,117,147,163,188,196,198,220,234,271,272,274,312,313,314,333,349,367,371,373,391,403,424,443,462,481,515,531,538,540,543,547,550,552,555,561,562,563,564,565,566,569,572,588,173,174,176,177,179,111,83,84,90,112,94,95,98,114,115,181,183,155,156,157,158,161,126,135,136,137,138,141,142,143,144,145,185,85,86,87,96,97,127,128,129,130,131,132,287,290,249,250,251,255,257,258,292,297,300,301,302,309,304,305,306,307,252,253,260,261,262,265,266,267,268,344,322,323,324,325,326,328,331,346,230,207,208,218,231,209,210,526,489,496,500,463,465,444,448,449,450,452,453,454,458,459,553,382,383,387,389,460,467,470,425,426,427,429,436,556,441,472,473,474,475,479,40,42,43,44,45,46,501,503,504,505,506,509,510,511,512,513,528,490,491,495,541,544,456,581,582,583,585,584,593,594,288,88,89],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/exception.py":[2,6,7,12,13,16,17,20,21,24,25,28,29,32,33,36,37,40,41,44,45,48,49,52,53,56,57,60,64,66,71,72,75,76,79,80,83,84,87,88,91,92,95,96,99,100,103,104,107,108,111,112,115,116,8,9],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/state_interface.py":[1,2,4,6,8,11,12,14,15,16,17,18,19,20,25,26,27,28,32,33,34,37,41,48,55,59,66,74,75,76,79,92,114,126,144,186,227,249,264,274,285,296,307,318,329,340,351,369,35,39,77,163,164,137,86,87,88,165,166,183,358,359,365,366,367,102,103,90,107,108,112,109,110,202,204,205,206,210,211,217,218,219,220,221,222,223,207,208,225,104,105,138,139,168,169,175,176,177,178,179,180,181,140,361,272,236,237,238,242,243,247,283,258,259,260,338,245,246,212,213,294,141],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_08_webfinger.py":[1,2,4,5,6,8,9,10,12,14,17,52,120,133,148,157,180,223,245,246,256,266,277,289,301,18,19,21,22,23,24,25,26,27,28,29,30,31,32,34,36,37,38,39,42,43,44,45,46,47,48,49,53,54,58,60,62,63,65,67,69,71,72,74,75,77,78,80,81,83,84,86,87,89,90,93,94,97,98,101,102,105,106,110,111,112,113,114,115,116,117,121,122,123,124,127,128,129,130,134,135,137,138,141,142,143,144,145,149,150,152,153,154,158,159,161,164,167,168,169,170,172,173,174,177,182,184,187,191,192,193,196,197,200,201,202,204,205,209,210,215,217,218,219,220,225,227,230,232,235,236,237,241,242,247,248,249,250,251,252,253,254,257,258,259,260,261,262,263,264,267,268,269,271,272,273,274,275,278,279,280,281,283,284,285,286,287,290,291,292,293,295,296,297,298,299,302,303,304,307,309,310],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/__init__.py":[5,9,12,15,18,21,25,26,29,30,31,35,36,37,38,39],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/webfinger.py":[1,2,4,5,6,7,9,10,12,14,16,17,18,19,20,23,26,27,28,29,30,31,32,33,36,42,65,75,147,37,38,40,83,86,87,91,92,93,94,95,100,103,104,143,144,145,101,96,97,98,116,117,124,127,140,141,67,68,69,72,70,73,128,129,130,132,133,137,138,105,106,107,108,110,111,112,120,121,122,123,149,152,153,163,154,155,156,157,158,159,160,161,43,44,48,49,50,51,52,53,54,55,57,61,62,63],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_09_client_auth.py":[1,2,3,5,6,7,8,9,10,11,15,16,20,21,22,23,24,26,27,29,30,31,32,35,39,45,53,65,77,78,91,100,110,111,118,128,139,148,178,179,187,206,228,229,249,250,279,296,297,329,330,362,363,66,67,68,69,70,72,55,56,57,58,59,60,61,62,40,42,79,80,82,83,85,86,87,92,93,95,96,98,102,104,105,107,112,113,114,46,48,49,50,116,119,120,122,123,125,36,126,129,131,132,133,135,136,137,140,141,143,145,146,149,150,151,152,153,154,157,158,159,160,163,164,165,166,167,169,173,174,175,180,181,182,184,185,188,189,191,192,194,195,196,197,198,199,201,202,203,204,207,208,209,210,211,212,214,215,216,217,218,219,221,222,224,225,230,231,232,233,234,236,237,238,240,241,242,243,244,245,246,251,252,253,255,256,258,259,260,261,262,264,265,266,267,268,269,271,272,273,274,276,280,282,283,285,286,287,288,289,290,291,292,293,298,299,300,301,302,304,305,307,308,309,310,311,313,314,315,316,317,318,320,321,322,324,325,331,332,333,334,335,337,338,340,341,342,343,344,346,347,348,349,350,351,353,354,355,356,358,359,364,365,368,369,370,372,373,375,376,379,380],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/service_factory.py":[1,2,3,4,6,9,33,10,11,12,14,15,16,17,18,20,21,22,24,25,26,27,28,19],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_10_oauth2_service.py":[1,2,6,7,8,9,12,13,19,25,26,38,51,65,79,80,98,107,119,140,160,161,186,191,198,199,216,223,228,20,21,22,29,30,31,32,34,35,36,39,40,41,42,43,44,45,46,52,53,54,55,56,57,58,59,66,67,68,69,70,71,72,83,84,85,87,88,89,90,91,92,94,95,96,99,101,102,103,110,112,113,114,115,121,122,124,125,126,127,128,129,130,131,132,133,138,142,143,145,147,148,149,150,151,152,153,163,166,167,170,171,172,173,174,175,177,178,180,181,182,183,184,187,188,189,192,193,194,202,203,204,206,207,208,209,210,211,212,213,214,217,218,219,220,224,225,230,231,232,234,235,236,237,239,240,241,242,243,246,247,249,250,251,253,254,255,256],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_13_oic_service.py":[1,2,4,5,6,7,8,9,10,13,15,18,19,21,22,25,26,33,34,37,39,41,42,43,45,46,47,49,50,52,53,62,63,74,85,97,107,118,128,138,158,174,181,198,219,237,257,258,272,284,296,309,310,327,337,356,375,384,385,407,412,418,520,550,564,573,574,586,591,599,609,610,646,652,659,688,716,725,746,747,760,769,770,783,791,792,806,816,844,861,880,899,66,67,69,70,71,72,76,77,79,80,81,87,88,90,91,92,95,99,100,102,103,104,109,110,112,113,114,116,119,120,121,122,123,124,125,129,130,131,132,133,134,135,139,140,141,142,143,144,145,146,148,149,150,151,152,153,154,155,159,160,162,164,165,166,168,169,170,172,175,176,177,178,179,182,183,184,186,187,189,190,191,193,194,195,196,199,200,201,203,205,206,209,210,211,213,214,215,216,217,220,221,222,224,225,227,228,229,231,232,233,234,235,239,240,241,243,244,245,247,249,250,251,253,254,261,263,264,265,268,269,270,274,275,277,278,279,282,286,287,289,290,291,294,298,299,301,302,303,306,313,314,316,317,319,320,321,322,324,325,328,330,331,332,333,339,340,342,343,344,345,346,347,348,349,350,358,359,361,363,364,365,366,367,368,369,376,377,378,379,380,381,387,389,390,391,394,395,396,397,398,399,400,403,404,405,408,409,410,413,414,415,419,422,424,425,426,427,428,429,430,431,432,433,434,435,436,437,438,439,440,441,442,443,444,445,446,447,448,449,450,451,452,453,454,455,456,458,459,460,461,463,464,465,466,468,469,470,472,473,474,476,477,478,479,481,482,483,484,486,487,488,490,491,493,494,496,497,498,499,500,501,502,503,504,505,506,508,509,510,511,521,524,526,527,528,529,530,531,532,533,534,535,537,538,539,540,551,552,554,555,558,559,576,578,579,580,581,583,584,587,588,589,593,594,595,596,597,600,601,602,603,604,605,606,612,614,615,616,617,619,620,621,622,623,624,627,630,631,634,635,637,565,567,569,570,639,641,642,643,644,647,648,649,650,653,654,655,656,657,662,663,664,665,666,668,671,672,674,675,677,678,680,682,683,684,691,692,693,694,695,697,700,702,703,705,706,708,709,711,713,714,717,718,719,720,721,722,723,727,729,730,732,734,735,736,737,738,740,741,742,743,749,751,752,753,754,756,757,758,761,762,763,764,765,766,772,774,775,776,777,779,780,781,784,785,786,787,788,794,796,797,798,799,800,802,803,804,807,808,809,810,811,812,818,819,820,821,824,825,826,828,829,830,832,833,839,840,841,846,847,848,849,851,852,855,856,857,858,863,864,865,866,867,869,870,873,874,875,876,877,882,883,884,886,887,890,891,892,894,895,896,901,902,903,905,906,909,910,911,912,913],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/registration.py":[1,3,4,6,7,9,11,15,16,17,18,19,20,24,41,53,71,96,97,98,99,100,101,102,103,104,106,115,130,143,107,108,109,110,111,112,113,25,27,28,29,30,31,36,38,116,117,120,121,122,123,125,126,127,128,42,43,44,45,50,63,64,65,68,72,76,79,80,81,85,86,87,88,93,131,132,133,138,141,66,46,47,48,90,91,82,83,73,75,144,147,148,149,150,151,152,153,154,156,157,158,159,160,161,162,163,167,168,169,139],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/provider_info_discovery.py":[1,3,4,6,7,9,11,15,17,19,20,22,24,25,27,29,30,31,32,34,35,36,37,40,43,44,48,72,73,74,75,77,82,91,78,79,80,83,84,105,108,110,112,113,114,115,116,118,119,131,132,133,156,135,136,144,145,146,147,148,149,137,139,140,159,160,163,164,165,167,171,172,169,170,166,161,174,175,85,120,121,124,125,126,127,128,129,57,58,61,62,67,69],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oauth2/__init__.py":[5,8,11,14],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oauth2/provider_info_discovery.py":[1,2,4,5,6,8,9,10,12,15,16,17,18,19,20,21,22,24,28,44,54,78,88,105,143,25,26,52,34,35,39,42,116,117,118,55,56,62,65,69,70,71,72,76,122,123,125,92,93,94,95,129,130,136,137,141,97,98,102,103,80,84,85,86],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_14_pkce.py":[1,3,4,5,6,8,9,10,11,12,13,14,17,19,23,24,27,29,32,33,36,37,38,41,42,71,85,95,109,110,138,45,46,47,49,50,52,53,58,59,60,61,62,64,66,67,69,72,73,74,75,78,80,82,83,86,87,88,90,91,96,97,98,99,100,102,103,104,113,114,116,117,119,120,125,126,127,128,129,131,133,134,136,139,140,141,143,145,146],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/add_on/__init__.py":[1,4,5,6,7],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/add_on/pkce.py":[1,3,4,6,7,8,10,13,62,80,85,95,96,98,99,102,104,105,106,24,26,27,32,33,35,36,40,42,44,46,51,52,54,56,57,59,72,73,74,75,76,77,81,82],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oauth2/utils.py":[1,4,17,49,6,7,14,19,21,24,25,44,46,51,52,8,9,10,26,27,32,33,34,35,37,39,40,42,38],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_15_oic_utils.py":[1,2,3,5,7,10,11,14,16,19,43,20,21,22,25,26,27,29,30,31,32,33,35,36,38,40,44,45,46,47,48,49],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/utils.py":[1,3,4,5,7,10,68,19,20,21,22,23,24,25,26,28,31,32,33,34,35,36,41,45,46,48,49,50,51,53,56,62,63,65,78,79,81,82,83,84,87,88],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_16_cc_oauth2_service.py":[1,3,4,5,7,10,11,30,42,14,15,17,19,20,21,22,23,24,25,27,28,31,32,33,34,35,36,37,43,44,45,46,47,48,49,51,52,53,54,55,57],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_17_read_registration.py":[1,2,4,5,6,7,9,10,11,13,14,17,18,41,20,22,23,24,26,27,28,29,30,31,32,35,36,37,38,39,42,44,46,48,49,50,51,52,53,54,55,56,57,58,59,60,63,64,65,66,67,68,69,72,73,75,77,78,79,80,81,82,83,84,85,88,89],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_20_conversation.py":[2,3,4,6,7,8,12,13,14,15,16,21,22,26,27,29,30,31,32,34,37,38,41,42,43,44,45,46,47,52,53,54,55,58,59,60,61,62,67,69,71,74,75,76,77,78,79,83,84,85,86,88,89,94,95,96,100,101,102,103,104,105,107,108,112,113,115,116,118,122,126,127,129,136,137,139,140,142,145,147,148,149,153,154,158,160,164,165,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,201,202,203,204,206,207,208,209,211,212,213,215,216,217,219,220,221,223,224,225,227,228,229,231,232,234,235,237,238,239,240,241,242,243,244,245,246,249,251,252,254,255,256,257,261,263,264,265,274,276,278,279,280,281,282,283,284,285,286,287,288,289,290,291,294,295,297,298,299,300,309,310,312,313,315,316,317,319,320,321,324,325,326,327,328,331,333,334,335,336,337,342,343,346,347,349,350,351,358,365,366,368,369,371,374,375,376,377,378,379,382,383,384,386,387,390,392,393,395,400,401,405,407,408,410,412,413,415,416,418,419,420],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_21_pushed_auth.py":[1,2,4,5,6,8,9,10,11,12,13,15,17,20,21,24,25,26,29,30,66,33,34,35,37,39,42,43,44,45,50,51,52,53,54,56,58,59,61,62,63,67,68,69,71,72,74,75,76,77,79,81],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/tests/test_30_persistence.py":[2,3,4,5,7,8,9,10,13,14,15,16,21,22,26,27,29,30,31,32,38,42,43,46,47,48,49,50,51,52,57,58,59,62,63,64,65,66,71,73,75,78,79,80,81,86,88,90,93,94,95,96,97,98,102,103,104,106,107,111,164,165,166,171,112,114,117,118,119,120,121,122,124,125,126,129,130,131,132,133,136,137,138,139,142,143,144,145,151,152,154,156,160,161,172,174,175,178,179,181,183,184,186,187,189,190,192,195,197,201,203,205,206,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,242,243,244,245,247,248,249,250,252,253,254,256,257,258,260,261,262,264,265,266,268,269,270,272,273,275,276,278,279,280,281,282,283,284,285,286,287,290,292,293,294,295,297,301,303,304,305,314,316,318,319,320,321,322,323,324,325,326,327,328,329,330,331,334,336,340,341,343,344,346,347,350,351,352,353,354,357,359,360,368,369,372,373,375,376,377,384,391,392,394,395,397,400,401,402,403,404,405,408,409,410,412,413,416,418,419,421,426,427,431,433,434,436,438,439,441,442,443,444],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/authorization.py":[1,3,4,5,7,8,9,10,11,12,15,17,20,21,22,23,25,32,45,65,107,124,142,169,198,26,27,28,29,30,204,206,207,208,211,219,220,221,222,224,225,226,229,46,47,48,49,61,63,33,34,35,36,37,41,42,43,66,69,70,76,77,83,84,85,87,90,91,92,93,94,98,105,177,178,179,180,182,186,187,188,189,195,196,78,79,99,102,103,191,193,144,108,109,110,111,112,113,117,118,119,120,121,122,145,147,148,150,151,152,153,154,155,156,158,161,162,164,165,100,167,131,132,133,137,138,139,140,53,54,59,55,56,57,227,38,39,71,72,73,212,213,214,215,216,217],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oauth2/authorization.py":[1,2,4,5,6,7,9,11,13,16,17,18,19,20,21,22,23,24,26,32,37,43,54,27,28,29,30,64,65,66,70,77,44,46,52,39,40,41],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/access_token.py":[1,3,4,5,7,8,9,11,13,16,17,18,19,22,26,61,81,23,24,32,36,37,38,41,49,50,51,52,54,55,56,59,62,63,64,65,75,76,77,79,82,83,84,85,86,67,68,69,70,42,43,44,45,46,47,73],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oauth2/access_token.py":[1,2,4,5,6,8,9,11,14,15,16,17,18,19,20,21,22,23,24,25,27,32,37,28,29,30,44,45,47,48,50,51,53,54,56,59,60,62],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/refresh_access_token.py":[1,3,6,7,8,9,11],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oauth2/refresh_access_token.py":[1,2,4,5,6,8,9,11,14,15,16,17,18,19,20,21,22,23,25,30,35,26,27,28,37,38,40,41,43,44,45,47,50,51,53],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/userinfo.py":[1,3,4,5,7,8,10,13,14,15,19,30,31,32,33,34,35,36,37,38,40,45,59,106,41,42,43,46,49,52,53,54,57,27,112,114,115,116,119,120,127,128,129,130,132,60,61,62,65,66,70,73,74,75,76,103,104,78,79,80,81,82,83,89,90,91,93,94,84,85,86,87,121,122,123,124,125],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/end_session.py":[1,3,4,6,7,9,11,14,15,16,17,18,19,20,21,23,30,53,65,24,25,26,27,28,38,39,40,41,44,45,49,51,54,55,58,59,63,66,67,70,72],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/read_registration.py":[1,3,4,5,7,9,12,13,14,15,16,17,18,19,21,27,37,39,40,41,42,43,46,22,23],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/check_id.py":[1,3,4,6,8,10,13,14,15,16,17,18,19,21,26,22,23,24,27,28,29,30],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/check_session.py":[1,3,4,6,8,10,13,14,15,16,17,18,19,21,26,22,23,24,27,28,29,30],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oauth2/client_credentials/__init__.py":[1],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oauth2/client_credentials/cc_access_token.py":[1,2,3,5,8,9,10,11,12,13,14,15,16,17,18,20,24,21,22,25,26,27],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oauth2/client_credentials/cc_refresh_access_token.py":[1,2,3,5,8,9,10,11,12,13,14,15,16,18,24,41,50,19,20,21,22,25,26,27,29,30,31,33,36,37,39,42,43,44,48],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/src/oidcservice/oidc/add_on/pushed_authorization.py":[1,3,4,5,7,9,12,54,62,63,65,67,68,69,70,73,19,22,25,26,27,29,30,31,32,34,37,38,39,42,43,44,45,46,47,48,50],"/home/wert/DEV3/OIDC-Project/JWTConnect-Python-OidcService/setup.py":[]}} \ No newline at end of file +!coverage.py: This is a private format, don't read it directly!{"lines":{"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/__init__.py":[1,2,5,6,7,8,10,11,13,15,16,17,21,22,23,24,25,27,29,30,32,35,45,48,60,42,57],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/service_context.py":[4,5,6,7,9,10,11,12,13,14,16,20,21,22,25,26,27,30,31,32,38,39,40,43,44,45,48,49,50,53,58,59,60,61,62,63,64,80,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,111,179,182,204,227,252,269,290,293,112,113,114,116,117,119,121,123,124,125,126,127,128,129,130,131,132,133,134,135,137,139,141,142,294,143,144,146,147,149,150,151,152,154,157,158,159,160,161,163,164,165,166,167,169,174,175,176,177,171,195,198,199,200,259,260,291,261,262,263,264,265,267,276,277,278,279,286,288,212,213,214,217,219,222,224,225,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,155,202,215,216,220],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/state_interface.py":[1,2,4,5,6,7,8,10,13,14,16,17,18,19,20,21,22,27,28,29,30,34,35,36,39,43,50,57,61,68,76,77,80,83,87,100,122,134,152,194,235,257,272,282,293,304,315,326,337,348,359,377,84,85,37,41,171,172,145,94,95,96,173,174,191,366,367,373,374,375,110,111,98,115,116,120,117,118,210,212,213,214,218,219,225,226,227,228,229,230,231,215,216,233,112,113,146,147,176,177,183,184,185,186,187,188,189,148,369,280,244,245,246,250,251,255,291,266,267,268,346,253,254,220,221,302,149],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/util.py":[1,2,3,4,6,7,9,11,13,14,15,18,44,67,74,89,28,29,30,31,32,35,36,37,54,55,57,58,33,91,76,82,83,86,92,93,41,39],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/service.py":[1,2,3,4,5,7,8,9,10,11,13,14,15,16,17,18,19,20,21,23,25,27,29,31,34,35,36,37,38,39,40,41,42,43,44,45,46,49,50,51,52,53,54,55,56,60,84,121,137,157,172,182,219,241,257,270,271,293,305,306,334,404,422,433,456,465,487,559,575,591,61,62,63,67,68,69,77,80,81,82,192,198,199,147,129,130,131,132,135,148,149,155,203,209,93,101,102,105,106,107,109,110,111,112,113,114,116,119,214,216,165,167,170,103,355,356,357,358,300,359,360,362,252,255,364,365,367,368,369,373,374,314,315,317,318,319,320,281,283,291,322,331,377,378,379,380,263,264,382,385,397,400,413,414,416,417,420,603,604,605,606,607,608,610,611,612,615,616,620,622,625,626,630,193,150,151,205,206,117,507,510,512,523,524,526,530,532,466,467,468,485,534,537,540,541,542,545,551,553,557,431,168,284,285,231,232,234,235,236,237,286,287,387,388,394,395,398,253,70,71,74,75,288,289,508,546,547,548,549,527,457,458,459,460,461,462,463,528,108,65,441,442,443,446,447,448,450,454,389,392,618,569,572,451,180,323,324,325,326,327,328,329],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/client_auth.py":[1,2,3,4,6,7,8,9,10,11,12,14,16,18,21,22,25,26,30,50,54,56,60,69,80,82,93,101,118,148,164,189,197,199,221,235,272,273,275,313,314,316,335,351,369,373,375,393,405,426,445,477,496,530,546,553,555,558,562,565,567,570,576,577,578,579,580,581,584,587,603,174,175,177,178,180,112,84,85,91,113,95,96,99,115,116,182,184,156,157,158,159,162,127,136,137,138,139,142,143,144,145,146,186,86,87,88,97,98,128,129,130,131,132,133,288,291,250,251,252,256,258,259,293,298,301,302,303,310,305,306,307,308,253,254,261,262,263,266,267,268,269,346,324,325,326,327,328,330,333,348,231,208,209,219,232,210,211,541,504,511,515,478,480,446,450,451,452,453,469,473,475,482,485,427,428,429,431,438,571,443,487,488,489,490,494,41,43,44,45,46,47,516,518,519,520,521,524,525,526,527,528,543,505,506,510,559,471,474,556,384,385,389,391,596,597,598,600,599,608,609,289,89,90],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/exception.py":[2,6,7,12,13,16,17,20,21,24,25,28,29,32,33,36,37,40,41,44,45,48,49,52,53,56,57,60,64,66,71,72,75,76,79,80,83,84,87,88,91,92,95,96,99,100,103,104,107,108,111,112,115,116,8,9],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/__init__.py":[5,9,12,15,18,21,25,26,29,30,31,35,36,37,38,39],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/webfinger.py":[1,2,4,5,6,7,9,10,12,14,16,17,18,19,20,23,26,27,28,29,30,31,32,33,36,42,65,75,147,37,38,40,83,86,87,91,92,93,94,95,100,103,104,143,144,145,101,96,97,98,116,117,124,127,140,141,67,68,69,72,70,73,128,129,130,132,133,137,138,105,106,107,108,110,111,112,120,121,122,123,149,152,153,163,154,155,156,157,158,159,160,161,43,44,48,49,50,51,52,53,54,55,57,61,62,63],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/service_factory.py":[1,2,3,4,6,9,33,10,11,12,14,15,16,17,18,20,21,22,24,25,26,27,28,19],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/registration.py":[1,3,4,6,7,9,11,15,16,17,18,19,20,24,41,53,71,96,97,98,99,100,101,102,103,104,106,115,130,143,107,108,109,110,111,112,113,25,27,28,29,30,31,36,38,116,117,120,121,122,123,125,126,127,128,42,43,44,45,50,63,64,65,68,72,76,79,80,81,85,86,87,88,93,131,132,133,138,141,66,46,47,48,90,91,82,83,73,75,144,147,148,149,150,151,152,153,154,156,157,158,159,160,161,162,163,167,168,169],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/provider_info_discovery.py":[1,3,4,6,7,9,11,15,17,19,20,22,24,25,27,29,30,31,32,34,35,36,37,40,43,44,48,72,73,74,75,77,82,91,78,79,80,83,84,105,108,110,112,113,114,115,116,118,119,131,132,133,156,135,136,144,145,146,147,148,149,137,139,140,159,160,163,164,165,167,171,172,169,170,166,161,174,175,85,120,121,124,125,126,127,128,129,57,58,61,62,67,69],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/__init__.py":[5,8,11,14],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/provider_info_discovery.py":[1,2,4,5,6,8,9,10,12,15,16,17,18,19,20,21,22,24,28,44,54,78,88,105,143,25,26,52,34,35,39,42,116,117,118,55,56,62,65,69,70,71,72,76,122,123,125,92,93,94,95,129,130,136,137,141,97,98,102,103,80,84,85,86],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/__init__.py":[1,4,5,6,7],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/pkce.py":[1,3,4,6,7,8,10,13,62,80,85,95,96,98,99,102,104,105,106,24,26,27,32,33,35,36,40,42,44,46,51,52,54,56,57,59,72,73,74,75,76,77,81,82],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/utils.py":[1,4,17,49,6,7,14,19,21,24,25,44,46,51,52,8,9,10,26,27,32,33,34,35,37,39,40,42,38],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/utils.py":[1,3,4,5,7,10,68,19,20,21,22,23,24,25,26,28,31,32,33,34,35,36,41,45,46,48,49,50,51,53,56,62,63,65,78,79,81,82,83,84,87,88],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/authorization.py":[1,3,4,5,7,8,9,10,11,12,15,17,20,21,22,23,25,32,45,65,107,124,142,179,208,26,27,28,29,30,214,216,217,218,221,222,223,225,226,233,234,235,236,238,239,242,46,47,48,49,61,63,33,34,35,36,37,41,42,43,66,69,70,76,77,83,84,85,87,90,91,92,93,94,98,105,187,188,189,190,192,196,197,198,199,205,206,78,79,99,102,103,201,203,145,108,109,110,111,112,113,117,118,119,120,121,122,146,148,149,151,154,155,157,158,159,160,161,163,165,168,171,172,174,175,100,177,131,132,133,137,138,139,140,53,54,59,55,56,57,240,38,39,71,72,73,227,228,229,230,231],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/authorization.py":[1,2,4,5,6,7,9,11,13,16,17,18,19,20,21,22,23,24,26,32,37,43,54,27,28,29,30,64,65,66,70,77,44,46,52,39,40,41],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/access_token.py":[1,2,4,5,6,8,9,10,12,14,17,18,19,20,25,29,64,85,26,27,35,39,40,41,44,45,52,53,54,55,57,58,62,65,66,67,68,69,79,80,81,83,86,87,88,89,90,71,72,73,74,46,47,48,49,50,77],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/access_token.py":[1,2,4,5,6,8,9,11,14,15,16,17,18,19,20,21,22,23,24,25,27,32,37,28,29,30,44,45,47,48,50,51,53,54,56,59,60,62],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/refresh_access_token.py":[1,3,6,7,8,9,11],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/refresh_access_token.py":[1,2,4,5,6,8,9,11,14,15,16,17,18,19,20,21,22,23,25,30,35,26,27,28,37,38,40,41,42,44,45,46,48,51,52,54],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/userinfo.py":[1,3,4,5,7,8,10,13,14,15,19,30,31,32,33,34,35,36,37,38,40,45,59,107,41,42,43,46,49,52,53,54,57,27,113,115,116,117,120,121,128,129,130,131,133,60,61,62,63,66,67,71,74,75,76,77,104,105,79,80,81,82,83,84,90,91,92,94,95,85,86,87,88,122,123,124,125,126],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/pushed_authorization.py":[1,3,4,5,7,9,12,54,65,66,68,70,71,72,73,76,19,22,25,26,27,29,30,31,32,34,37,38,39,42,43,44,45,46,47,48,50],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/dpop.py":[1,2,4,5,6,7,9,11,14,17,18,19,21,22,23,24,26,27,29,39,48,55,62,88,89,146,154,157,159,102,103,105,106,107,110,111,112,113,114,116,120,121,122,125,126,127,128,129,130,131,134,30,31,40,42,43,44,46,33,34,135,136,56,57,58,59,60,138,141,143],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/client_credentials/cc_refresh_access_token.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/client_credentials/cc_access_token.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/client_credentials/__init__.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/end_session.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/check_id.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/read_registration.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/check_session.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/status_check.py":[]}} \ No newline at end of file diff --git a/setup.py b/setup.py index 896e528..b72d163 100755 --- a/setup.py +++ b/setup.py @@ -69,7 +69,7 @@ "Topic :: Software Development :: Libraries :: Python Modules"], install_requires=[ "pyyaml>=5.1.0", - 'oidcmsg>=1.1.0', + 'oidcmsg>=1.2.0', 'requests' ], tests_require=[ diff --git a/src/oidcservice/__init__.py b/src/oidcservice/__init__.py index af7fd99..94887d0 100755 --- a/src/oidcservice/__init__.py +++ b/src/oidcservice/__init__.py @@ -8,7 +8,7 @@ import random as rnd __author__ = 'Roland Hedberg' -__version__ = '1.1.1' +__version__ = '1.2.0' OIDCONF_PATTERN = "{}/.well-known/openid-configuration" CC_METHOD = { diff --git a/src/oidcservice/client_auth.py b/src/oidcservice/client_auth.py index bfadd4f..b7f880f 100755 --- a/src/oidcservice/client_auth.py +++ b/src/oidcservice/client_auth.py @@ -263,7 +263,7 @@ def find_token(request, token_type, service, **kwargs): except KeyError: # I should pick the latest acquired token, this should be the right # order for that. - _arg = service.multiple_extend_request_args( + _arg = service.service_context.state.multiple_extend_request_args( {}, kwargs['key'], ['access_token'], ['auth_response', 'token_response', 'refresh_token_response']) return _arg['access_token'] diff --git a/src/oidcservice/oauth2/access_token.py b/src/oidcservice/oauth2/access_token.py index d7f5239..216c11f 100644 --- a/src/oidcservice/oauth2/access_token.py +++ b/src/oidcservice/oauth2/access_token.py @@ -32,7 +32,7 @@ def __init__(self, service_context, client_authn_factory=None, conf=None): def update_service_context(self, resp, key='', **kwargs): if 'expires_in' in resp: resp['__expires_at'] = time_sans_frac() + int(resp['expires_in']) - self.store_item(resp, 'token_response', key) + self.service_context.state.store_item(resp, 'token_response', key) def oauth_pre_construct(self, request_args=None, post_args=None, **kwargs): """ @@ -44,11 +44,11 @@ def oauth_pre_construct(self, request_args=None, post_args=None, **kwargs): _state = get_state_parameter(request_args, kwargs) parameters = list(self.msg_type.c_param.keys()) - _args = self.extend_request_args({}, oauth2.AuthorizationRequest, - 'auth_request', _state, parameters) + _args = self.service_context.state.extend_request_args({}, oauth2.AuthorizationRequest, + 'auth_request', _state, parameters) - _args = self.extend_request_args(_args, oauth2.AuthorizationResponse, - 'auth_response', _state, parameters) + _args = self.service_context.state.extend_request_args(_args, oauth2.AuthorizationResponse, + 'auth_response', _state, parameters) if "grant_type" not in _args: _args["grant_type"] = "authorization_code" diff --git a/src/oidcservice/oauth2/authorization.py b/src/oidcservice/oauth2/authorization.py index c081502..9dfb94c 100644 --- a/src/oidcservice/oauth2/authorization.py +++ b/src/oidcservice/oauth2/authorization.py @@ -32,12 +32,12 @@ def __init__(self, service_context, client_authn_factory=None, conf=None): def update_service_context(self, resp, key='', **kwargs): if 'expires_in' in resp: resp['__expires_at'] = time_sans_frac() + int(resp['expires_in']) - self.store_item(resp, 'auth_response', key) + self.service_context.state.store_item(resp, 'auth_response', key) def store_auth_request(self, request_args=None, **kwargs): """Store the authorization request in the state DB.""" _key = get_state_parameter(request_args, kwargs) - self.store_item(request_args, 'auth_request', _key) + self.service_context.state.store_item(request_args, 'auth_request', _key) return request_args def gather_request_args(self, **kwargs): diff --git a/src/oidcservice/oauth2/client_credentials/cc_access_token.py b/src/oidcservice/oauth2/client_credentials/cc_access_token.py index 915210c..64c4f50 100644 --- a/src/oidcservice/oauth2/client_credentials/cc_access_token.py +++ b/src/oidcservice/oauth2/client_credentials/cc_access_token.py @@ -24,4 +24,4 @@ def __init__(self, service_context, client_authn_factory=None, conf=None): def update_service_context(self, resp, key='cc', **kwargs): if 'expires_in' in resp: resp['__expires_at'] = time_sans_frac() + int(resp['expires_in']) - self.store_item(resp, 'token_response', key) + self.service_context.state.store_item(resp, 'token_response', key) diff --git a/src/oidcservice/oauth2/client_credentials/cc_refresh_access_token.py b/src/oidcservice/oauth2/client_credentials/cc_refresh_access_token.py index 5c9cedf..fff4f41 100644 --- a/src/oidcservice/oauth2/client_credentials/cc_refresh_access_token.py +++ b/src/oidcservice/oauth2/client_credentials/cc_refresh_access_token.py @@ -22,13 +22,15 @@ def __init__(self, service_context, client_authn_factory=None, conf=None): self.post_construct.append(self.cc_post_construct) def cc_pre_construct(self, request_args=None, **kwargs): + _state_id = kwargs.get("state", "cc") parameters = ['refresh_token'] - _args = self.extend_request_args({}, oauth2.AccessTokenResponse, - 'token_response', 'cc', parameters) + _state_interface = self.service_context.state + _args = _state_interface.extend_request_args({}, oauth2.AccessTokenResponse, + 'token_response', _state_id, parameters) - _args = self.extend_request_args(_args, oauth2.AccessTokenResponse, - 'refresh_token_response', 'cc', - parameters) + _args = _state_interface.extend_request_args(_args, oauth2.AccessTokenResponse, + 'refresh_token_response', _state_id, + parameters) if request_args is None: request_args = _args @@ -50,4 +52,4 @@ def cc_post_construct(self, request_args, **kwargs): def update_service_context(self, resp, key='cc', **kwargs): if 'expires_in' in resp: resp['__expires_at'] = time_sans_frac() + int(resp['expires_in']) - self.store_item(resp, 'token_response', key) + self.service_context.state.store_item(resp, 'token_response', key) diff --git a/src/oidcservice/oauth2/refresh_access_token.py b/src/oidcservice/oauth2/refresh_access_token.py index dae2bf3..f100b8c 100644 --- a/src/oidcservice/oauth2/refresh_access_token.py +++ b/src/oidcservice/oauth2/refresh_access_token.py @@ -30,19 +30,20 @@ def __init__(self, service_context, client_authn_factory=None, conf=None): def update_service_context(self, resp, key='', **kwargs): if 'expires_in' in resp: resp['__expires_at'] = time_sans_frac() + int(resp['expires_in']) - self.store_item(resp, 'token_response', key) + self.service_context.state.store_item(resp, 'token_response', key) def oauth_pre_construct(self, request_args=None, **kwargs): """Preconstructor of request arguments""" _state = get_state_parameter(request_args, kwargs) parameters = list(self.msg_type.c_param.keys()) - _args = self.extend_request_args({}, oauth2.AccessTokenResponse, - 'token_response', _state, parameters) + _si = self.service_context.state + _args = _si.extend_request_args({}, oauth2.AccessTokenResponse, + 'token_response', _state, parameters) - _args = self.extend_request_args(_args, oauth2.AccessTokenResponse, - 'refresh_token_response', _state, - parameters) + _args = _si.extend_request_args(_args, oauth2.AccessTokenResponse, + 'refresh_token_response', _state, + parameters) if request_args is None: request_args = _args diff --git a/src/oidcservice/oidc/access_token.py b/src/oidcservice/oidc/access_token.py index 7d2e128..fe60705 100644 --- a/src/oidcservice/oidc/access_token.py +++ b/src/oidcservice/oidc/access_token.py @@ -1,4 +1,5 @@ import logging +from typing import Optional from oidcmsg import oidc from oidcmsg.oidc import verified_claim_name @@ -18,8 +19,10 @@ class AccessToken(access_token.AccessToken): response_cls = oidc.AccessTokenResponse error_msg = oidc.ResponseMessage - def __init__(self, service_context, client_authn_factory=None, - conf=None): + def __init__(self, + service_context, + client_authn_factory=None, + conf: Optional[dict]=None): access_token.AccessToken.__init__(self, service_context, client_authn_factory=client_authn_factory, conf=conf) @@ -38,8 +41,8 @@ def gather_verify_arguments(self): 'skew': _ctx.clock_skew, } - if 'registration_response' in _ctx: - _reg_resp = _ctx.get('registration_response') + _reg_resp = _ctx.get('registration_response') + if _reg_resp: for attr, param in IDT2REG.items(): try: kwargs[attr] = _reg_resp[param] @@ -51,32 +54,33 @@ def gather_verify_arguments(self): except KeyError: pass - if 'behaviour' in _ctx: - _verify_args = _ctx.get('behaviour').get("verify_args") + _verify_args = _ctx.get('behaviour').get("verify_args") + if _verify_args: if _verify_args: kwargs.update(_verify_args) return kwargs def update_service_context(self, resp, key='', **kwargs): + _state_interface = self.service_context.state try: _idt = resp[verified_claim_name('id_token')] except KeyError: pass else: try: - if self.get_state_by_nonce(_idt['nonce']) != key: + if _state_interface.get_state_by_nonce(_idt['nonce']) != key: raise ParameterError('Someone has messed with "nonce"') except KeyError: raise ValueError('Invalid nonce value') - self.store_sub2state(_idt['sub'], key) + _state_interface.store_sub2state(_idt['sub'], key) if 'expires_in' in resp: resp['__expires_at'] = time_sans_frac() + int( resp['expires_in']) - self.store_item(resp, 'token_response', key) + _state_interface.store_item(resp, 'token_response', key) def get_authn_method(self): try: diff --git a/src/oidcservice/oidc/add_on/pkce.py b/src/oidcservice/oidc/add_on/pkce.py index fa86531..517a748 100644 --- a/src/oidcservice/oidc/add_on/pkce.py +++ b/src/oidcservice/oidc/add_on/pkce.py @@ -49,7 +49,7 @@ def add_code_challenge(request_args, service, **kwargs): 'PKCE Transformation method:{}'.format(_method)) _item = Message(code_verifier=code_verifier, code_challenge_method=_method) - service.store_item(_item, 'pkce', request_args['state']) + service.service_context.state.store_item(_item, 'pkce', request_args['state']) request_args.update( { @@ -72,7 +72,7 @@ def add_code_verifier(request_args, service, **kwargs): _state = request_args.get('state') if _state is None: _state = kwargs.get('state') - _item = service.get_item(Message, 'pkce', _state) + _item = service.service_context.state.get_item(Message, 'pkce', _state) request_args.update({'code_verifier': _item['code_verifier']}) return request_args diff --git a/src/oidcservice/oidc/authorization.py b/src/oidcservice/oidc/authorization.py index b951f37..876afe8 100644 --- a/src/oidcservice/oidc/authorization.py +++ b/src/oidcservice/oidc/authorization.py @@ -38,7 +38,7 @@ def set_state(self, request_args, **kwargs): except KeyError: _state = '' - request_args['state'] = self.create_state( + request_args['state'] = self.service_context.state.create_state( self.service_context.get('issuer'), _state) return request_args, {} @@ -51,16 +51,16 @@ def update_service_context(self, resp, key='', **kwargs): # If there is a verified ID Token then we have to do nonce # verification try: - if self.get_state_by_nonce(_idt['nonce']) != key: + if self.service_context.state.get_state_by_nonce(_idt['nonce']) != key: raise ParameterError('Someone has messed with "nonce"') except KeyError: raise ValueError('Missing nonce value') - self.store_sub2state(_idt['sub'], key) + self.service_context.state.store_sub2state(_idt['sub'], key) if 'expires_in' in resp: resp['__expires_at'] = time_sans_frac() + int(resp['expires_in']) - self.store_item(resp.to_json(), 'auth_response', key) + self.service_context.state.store_item(resp.to_json(), 'auth_response', key) def oidc_pre_construct(self, request_args=None, post_args=None, **kwargs): if request_args is None: @@ -187,7 +187,7 @@ def oidc_post_construct(self, req, **kwargs): if 'openid' in req['scope']: _response_type = req['response_type'][0] if 'id_token' in _response_type or 'code' in _response_type: - self.store_nonce2state(req['nonce'], req['state']) + self.service_context.state.store_nonce2state(req['nonce'], req['state']) if 'offline_access' in req['scope']: if 'prompt' not in req: @@ -202,7 +202,7 @@ def oidc_post_construct(self, req, **kwargs): self.construct_request_parameter(req, _request_method, **kwargs) - self.store_item(req, 'auth_request', req['state']) + self.service_context.state.store_item(req, 'auth_request', req['state']) return req def gather_verify_arguments(self): @@ -222,8 +222,8 @@ def gather_verify_arguments(self): if _client_id: kwargs['client_id'] = _client_id - if 'registration_response' in _ctx: - _reg_res = _ctx.get('registration_response') + _reg_res = _ctx.get('registration_response') + if _reg_res: for attr, param in IDT2REG.items(): try: kwargs[attr] = _reg_res[param] @@ -235,9 +235,8 @@ def gather_verify_arguments(self): except KeyError: pass - if 'behaviour' in _ctx: - _verify_args = _ctx.get('behaviour').get("verify_args") - if _verify_args: - kwargs.update(_verify_args) + _verify_args = _ctx.get('behaviour').get("verify_args") + if _verify_args: + kwargs.update(_verify_args) return kwargs diff --git a/src/oidcservice/oidc/check_id.py b/src/oidcservice/oidc/check_id.py index a6f2847..0caf69e 100644 --- a/src/oidcservice/oidc/check_id.py +++ b/src/oidcservice/oidc/check_id.py @@ -24,7 +24,7 @@ def __init__(self, service_context, client_authn_factory=None, conf=None): self.pre_construct = [self.oidc_pre_construct] def oidc_pre_construct(self, request_args=None, **kwargs): - request_args = self.multiple_extend_request_args( + request_args = self.service_context.state.multiple_extend_request_args( request_args, kwargs['state'], ['id_token'], ['auth_response', 'token_response', 'refresh_token_response']) return request_args, {} diff --git a/src/oidcservice/oidc/check_session.py b/src/oidcservice/oidc/check_session.py index e29ebc7..c1c63d5 100644 --- a/src/oidcservice/oidc/check_session.py +++ b/src/oidcservice/oidc/check_session.py @@ -24,7 +24,7 @@ def __init__(self, service_context, client_authn_factory=None, conf=None): self.pre_construct = [self.oidc_pre_construct] def oidc_pre_construct(self, request_args=None, **kwargs): - request_args = self.multiple_extend_request_args( + request_args = self.service_context.state.multiple_extend_request_args( request_args, kwargs['state'], ['id_token'], ['auth_response', 'token_response', 'refresh_token_response']) return request_args, {} diff --git a/src/oidcservice/oidc/end_session.py b/src/oidcservice/oidc/end_session.py index f8ff8ef..a6a6823 100644 --- a/src/oidcservice/oidc/end_session.py +++ b/src/oidcservice/oidc/end_session.py @@ -35,7 +35,7 @@ def get_id_token_hint(self, request_args=None, **kwargs): :param kwargs: :return: """ - request_args = self.multiple_extend_request_args( + request_args = self.service_context.state.multiple_extend_request_args( request_args, kwargs['state'], ['id_token'], ['auth_response', 'token_response', 'refresh_token_response'], orig=True @@ -67,6 +67,6 @@ def add_state(self, request_args=None, **kwargs): request_args['state'] = rndstr(32) # As a side effect bind logout state to session state - self.store_logout_state2state(request_args['state'], kwargs['state']) + self.service_context.state.store_logout_state2state(request_args['state'], kwargs['state']) return request_args, {} diff --git a/src/oidcservice/oidc/userinfo.py b/src/oidcservice/oidc/userinfo.py index f4f4e9f..e417828 100644 --- a/src/oidcservice/oidc/userinfo.py +++ b/src/oidcservice/oidc/userinfo.py @@ -49,7 +49,7 @@ def oidc_pre_construct(self, request_args=None, **kwargs): if "access_token" in request_args: pass else: - request_args = self.multiple_extend_request_args( + request_args = self.service_context.state.multiple_extend_request_args( request_args, kwargs['state'], ['access_token'], ['auth_response', 'token_response', 'refresh_token_response'] ) @@ -57,7 +57,8 @@ def oidc_pre_construct(self, request_args=None, **kwargs): return request_args, {} def post_parse_response(self, response, **kwargs): - _args = self.multiple_extend_request_args( + _state_interface = self.service_context.state + _args = _state_interface.multiple_extend_request_args( {}, kwargs['state'], ['id_token'], ['auth_response', 'token_response', 'refresh_token_response'] ) @@ -100,7 +101,7 @@ def post_parse_response(self, response, **kwargs): "url": spec["endpoint"] } - self.store_item(response, 'user_info', kwargs['state']) + _state_interface.store_item(response, 'user_info', kwargs['state']) return response def gather_verify_arguments(self): diff --git a/src/oidcservice/service.py b/src/oidcservice/service.py index e53684c..abdbe6a 100644 --- a/src/oidcservice/service.py +++ b/src/oidcservice/service.py @@ -1,17 +1,24 @@ """ The basic Service class upon which all the specific services are built. """ import logging +from typing import Optional +from typing import Union from urllib.parse import urlparse from cryptojwt.jwt import JWT +from oidcmsg.impexp import ImpExp from oidcmsg.message import Message -from oidcmsg.oauth2 import ResponseMessage, is_error_message +from oidcmsg.oauth2 import ResponseMessage +from oidcmsg.oauth2 import is_error_message from oidcservice import util from oidcservice.client_auth import factory as ca_factory from oidcservice.exception import ResponseError from oidcservice.state_interface import StateInterface -from oidcservice.util import (JOSE_ENCODED, JSON_ENCODED, URL_ENCODED, - get_http_body, get_http_url) +from oidcservice.util import JOSE_ENCODED +from oidcservice.util import JSON_ENCODED +from oidcservice.util import URL_ENCODED +from oidcservice.util import get_http_body +from oidcservice.util import get_http_url __author__ = 'Roland Hedberg' @@ -24,7 +31,7 @@ REQUEST_INFO = 'Doing request with: URL:{}, method:{}, data:{}, https_args:{}' -class Service(StateInterface): +class Service(ImpExp): """The basic Service class.""" msg_type = Message response_cls = Message @@ -38,10 +45,20 @@ class Service(StateInterface): request_body_type = 'urlencoded' response_body_type = 'json' + parameter = { + 'msg_type': object, + 'response_cls': object, + 'error_msg': object, + 'default_authn_method': None, + 'http_method': None, + 'request_body_type': None, + 'response_body_type': None, + 'state': StateInterface + } + def __init__(self, service_context, conf=None, client_authn_factory=None, **kwargs): - StateInterface.__init__(self, service_context.state_db) - + ImpExp.__init__(self) if client_authn_factory is None: self.client_authn_factory = ca_factory else: @@ -62,6 +79,7 @@ def __init__(self, service_context, conf=None, # pull in all the modifiers self.pre_construct = [] self.post_construct = [] + self.construct_extra_headers = [] def gather_request_args(self, **kwargs): """ @@ -84,23 +102,19 @@ def gather_request_args(self, **kwargs): if prop in ar_args: continue - try: - ar_args[prop] = getattr(self.service_context, prop) - except AttributeError: - val = self.service_context.get(prop) - if val: - ar_args[prop] = val - else: - try: - ar_args[prop] = self.conf['request_args'][prop] - except KeyError: - try: - ar_args[prop] = self.service_context.register_args[prop] - except KeyError: - try: - ar_args[prop] = self.default_request_args[prop] - except KeyError: - pass + val = self.service_context.get(prop) + if not val: + if "request_args" in self.conf: + val = self.conf['request_args'].get(prop) + if not val: + val = self.service_context.register_args.get(prop) + if not val: + val = self.default_request_args.get(prop) + if not val: + val = self.service_context.behaviour.get(prop) + + if val: + ar_args[prop] = val return ar_args @@ -163,6 +177,7 @@ def update_service_context(self, resp, key='', **kwargs): :param key: The key under which the response should be stored :param kwargs: Extra key word arguments """ + pass def construct(self, request_args=None, **kwargs): """ @@ -250,7 +265,10 @@ def get_endpoint(self): return self.service_context.get('provider_info')[self.endpoint_name] - def get_authn_header(self, request, authn_method, **kwargs): + def get_authn_header(self, + request: Union[dict, Message], + authn_method: Optional[str] = '', + **kwargs) -> dict: """ Construct an authorization specification to be sent in the HTTP header. @@ -272,7 +290,7 @@ def get_authn_header(self, request, authn_method, **kwargs): return headers - def get_authn_method(self): + def get_authn_method(self) -> str: """ Find the method that the client should use to authenticate against a service. @@ -281,6 +299,37 @@ def get_authn_method(self): """ return self.default_authn_method + def get_headers(self, + request: Union[dict, Message], + http_method: str, + authn_method: Optional[str] = '', + **kwargs) -> dict: + """ + + :param request: + :param authn_method: + :param kwargs: + :return: + """ + if not authn_method: + authn_method = self.get_authn_method() + + _headers = self.get_authn_header(request, + authn_method=authn_method, + authn_endpoint=self.endpoint_name, + **kwargs) + + for meth in self.construct_extra_headers: + _headers = meth(self.service_context, + headers=_headers, + request=request, + authn_method=authn_method, + service_endpoint=self.endpoint_name, + http_method=http_method, + **kwargs) + + return _headers + def get_request_parameters(self, request_args=None, method="", request_body_type="", authn_method='', **kwargs): """ @@ -311,8 +360,9 @@ def get_request_parameters(self, request_args=None, method="", request_body_type = self.request_body_type request = self.construct_request(request_args=request_args, **kwargs) + LOGGER.debug("Request: %s", request) - _info = {'method': method} + _info = {'method': method, "request": request} _args = kwargs.copy() if self.service_context.get('issuer'): @@ -320,9 +370,8 @@ def get_request_parameters(self, request_args=None, method="", # Client authentication by usage of the Authorization HTTP header # or by modifying the request object - _headers = self.get_authn_header(request, authn_method, - authn_endpoint=self.endpoint_name, - **_args) + _headers = self.get_headers(request, http_method=method, + authn_method=authn_method, **_args) # Find out where to send this request try: @@ -559,9 +608,9 @@ def init_services(service_definitions, service_context, client_authn_factory=Non kwargs = {} kwargs.update({ - 'service_context': service_context, - 'client_authn_factory': client_authn_factory - }) + 'service_context': service_context, + 'client_authn_factory': client_authn_factory + }) if isinstance(service_configuration['class'], str): _srv = util.importer(service_configuration['class'])(**kwargs) diff --git a/src/oidcservice/service_context.py b/src/oidcservice/service_context.py index dc58b9b..c41a7bb 100644 --- a/src/oidcservice/service_context.py +++ b/src/oidcservice/service_context.py @@ -1,21 +1,20 @@ """ Implements a service context. A Service context is used to keep information that are -common to all the services by an OpenID Connect Relying Party. +common between all the services that are used by OAuth2 client or OpenID Connect Relying Party. """ import copy import hashlib import os -from cryptojwt.jwk.rsa import RSAKey, import_private_rsa_key_from_file +from cryptojwt.jwk.rsa import RSAKey +from cryptojwt.jwk.rsa import import_private_rsa_key_from_file from cryptojwt.key_bundle import KeyBundle -from cryptojwt.key_jar import build_keyjar from cryptojwt.utils import as_bytes from oidcmsg.context import OidcContext -# This represents a map between the local storage of algorithm choices -# and how they are represented in a provider info response. -from oidcmsg.message import Message from oidcmsg.oidc import RegistrationRequest +from oidcservice.state_interface import StateInterface + CLI_REG_MAP = { "userinfo": { "sign": "userinfo_signed_response_alg", @@ -66,16 +65,16 @@ } -def add_issuer(conf, issuer): - res = {} - for key, val in conf.items(): - if key == 'abstract_storage_cls': - res[key] = val - else: - _val = copy.deepcopy(val) - _val['issuer'] = issuer - res[key] = _val - return res +# def add_issuer(conf, issuer): +# res = {} +# for key, val in conf.items(): +# if key == 'abstract_storage_cls': +# res[key] = val +# else: +# _val = copy.deepcopy(val) +# _val['issuer'] = issuer +# res[key] = _val +# return res class ServiceContext(OidcContext): @@ -85,23 +84,42 @@ class ServiceContext(OidcContext): from dynamic provider info discovery or client registration. But information is also picked up during the conversation with a server. """ - - def __init__(self, keyjar=None, config=None, **kwargs): + parameter = OidcContext.parameter.copy() + parameter.update({ + "config": None, + "kid": None, + "base_url": None, + "requests_dir": None, + "register_args": None, + "allow": None, + "client_preferences": None, + "args": None, + "add_on": None, + "httpc_params": None, + 'client_secret': None, + 'client_id': None, + 'redirect_uris': None, + 'provider_info': None, + 'behaviour': None, + 'callback': None, + 'issuer': None, + 'clock_skew': None, + 'verify_args': None, + 'state': StateInterface + }) + + def __init__(self, base_url="", keyjar=None, config=None, state=None, **kwargs): if config is None: config = {} self.config = config OidcContext.__init__(self, config, keyjar, entity_id=config.get('client_id', '')) - - # For my Dev environment - self.state_db = None - - self.add_boxes({'state': 'state_db'}, self.db_conf) + self.state = state or StateInterface() self.kid = {"sig": {}, "enc": {}} + self.base_url = base_url # Below so my IDE won't complain - self.base_url = '' self.requests_dir = '' self.register_args = {} self.allow = {} @@ -109,6 +127,12 @@ def __init__(self, keyjar=None, config=None, **kwargs): self.args = {} self.add_on = {} self.httpc_params = {} + self.issuer = "" + self.client_id = "" + self.client_secret = "" + self.behaviour = {} + self.provider_info = {} + self.redirect_uris = [] _def_value = copy.deepcopy(DEFAULT_VALUE) # Dynamic information @@ -119,6 +143,9 @@ def __init__(self, keyjar=None, config=None, **kwargs): if param == 'client_secret': self.keyjar.add_symmetric('', _val) + if not self.issuer: + self.issuer = self.provider_info.get("issuer", "") + try: self.clock_skew = config['clock_skew'] except KeyError: @@ -212,7 +239,7 @@ def import_keys(self, keyspec): if typ == 'rsa': for fil in files: _key = RSAKey( - key=import_private_rsa_key_from_file(fil), + priv_key=import_private_rsa_key_from_file(fil), use='sig') _bundle = KeyBundle() _bundle.append(_key) @@ -261,13 +288,7 @@ def get_enc_alg_enc(self, typ): return res def get(self, key, default=None): - return self.db.get(key, default) + return getattr(self, key, default) def set(self, key, value): - if isinstance(value, Message): - self.db[key] = value.to_dict() - else: - self.db[key] = value - - def __contains__(self, item): - return item in self.db + setattr(self, key, value) diff --git a/src/oidcservice/state_interface.py b/src/oidcservice/state_interface.py index 802cb42..4360c1e 100644 --- a/src/oidcservice/state_interface.py +++ b/src/oidcservice/state_interface.py @@ -1,8 +1,10 @@ """A database interface for storing state information.""" import json -from oidcmsg.message import (SINGLE_OPTIONAL_JSON, SINGLE_REQUIRED_STRING, - Message) +from oidcmsg.impexp import ImpExp +from oidcmsg.message import SINGLE_OPTIONAL_JSON +from oidcmsg.message import SINGLE_REQUIRED_STRING +from oidcmsg.message import Message from oidcmsg.oidc import verified_claim_name from oidcservice import rndstr @@ -71,10 +73,16 @@ def __delitem__(self, key): pass -class StateInterface: +class StateInterface(ImpExp): """A more powerful interface to a state DB.""" - def __init__(self, state_db): - self.state_db = state_db + + parameter = { + "_db": None + } + + def __init__(self): + ImpExp.__init__(self) + self._db = {} def get_state(self, key): """ @@ -83,7 +91,7 @@ def get_state(self, key): :param key: Key into the state database :return: A :py:class:´oidcservice.state_interface.State` instance """ - _data = self.state_db.get(key) + _data = self._db.get(key) if not _data: raise KeyError(key) @@ -109,7 +117,7 @@ def store_item(self, item, item_type, key): except AttributeError: _state[item_type] = item - self.state_db[key] = _state.to_json() + self._db[key] = _state.to_json() def get_iss(self, key): """ @@ -233,9 +241,9 @@ def store_x2state(self, value, state, xtyp): :param state: The state value :param xtyp: The type of value x is (e.g. nonce, ...) """ - self.state_db[KEY_PATTERN[xtyp].format(value)] = state + self._db[KEY_PATTERN[xtyp].format(value)] = state try: - _val = self.state_db.get("ref{}ref".format(state)) + _val = self._db.get("ref{}ref".format(state)) except KeyError: _val = None @@ -244,7 +252,7 @@ def store_x2state(self, value, state, xtyp): else: refs = json.loads(_val) refs[xtyp] = value - self.state_db["ref{}ref".format(state)] = json.dumps(refs) + self._db["ref{}ref".format(state)] = json.dumps(refs) def get_state_by_x(self, value, xtyp): """ @@ -255,7 +263,7 @@ def get_state_by_x(self, value, xtyp): :param value: The value :return: The state value """ - _state = self.state_db.get(KEY_PATTERN[xtyp].format(value)) + _state = self._db.get(KEY_PATTERN[xtyp].format(value)) if _state: return _state @@ -363,7 +371,7 @@ def create_state(self, iss, key=''): 'Invalid format. Leading and trailing "__" not allowed') _state = State(iss=iss) - self.state_db[key] = _state.to_json() + self._db[key] = _state.to_json() return key def remove_state(self, state): @@ -372,8 +380,8 @@ def remove_state(self, state): :param state: Key to the state """ - self.state_db.delete(state) - refs = json.loads(self.state_db.get("ref{}ref".format(state))) + del self._db[state] + refs = json.loads(self._db_db.get("ref{}ref".format(state))) if refs: for xtyp, _val in refs.items(): - self.state_db.delete(KEY_PATTERN[xtyp].format(_val)) + del self._db[KEY_PATTERN[xtyp].format(_val)] diff --git a/tests/request123456.jwt b/tests/request123456.jwt index f835f8c..d4598ba 100644 --- a/tests/request123456.jwt +++ b/tests/request123456.jwt @@ -1 +1 @@ -eyJhbGciOiJSUzI1NiIsImtpZCI6ImFWODBkazlpZG1sbU1YVlBkMUJYV2xGcGIwZFdZVnBHYkRkVVYxSlFWWGRoV0cxVU9HeFNaRkZCYXcifQ.eyJyZXNwb25zZV90eXBlIjogImNvZGUiLCAic3RhdGUiOiAic3RhdGUiLCAicmVkaXJlY3RfdXJpIjogImh0dHBzOi8vZXhhbXBsZS5jb20vY2xpL2F1dGh6X2NiIiwgInNjb3BlIjogIm9wZW5pZCIsICJub25jZSI6ICJUVlE4R3FTb3pyY3dPcHpud0F1aGhZSW5kRTlaN0o1MyIsICJjbGllbnRfaWQiOiAiY2xpZW50X2lkIiwgImlzcyI6ICJjbGllbnRfaWQiLCAiaWF0IjogMTU5MjIzMjU0NywgImF1ZCI6IFsiaHR0cHM6Ly9leGFtcGxlLmNvbSJdfQ.giofJ-3wLrWGqiruj534rPBM-eMaHwxmrVFR2a0t3IOtfUaw8WEcxfyZgayHkH-eieG1GxB-NiHIIJYzV--PdAgzIQ-ZMGEAYKAm-mspb5Xks1pqTzk4qjorwNcb4dJ3gdvtLVsGQsBjUXcxFAWuZNqlNQMB0NDP-bbl18rG0L6xmm-JeRbJDNrmSdhcn8PcMugg8uJBqopx9v-g5fOCfS4y_MDUtwnZfXUuQXYKcq7PAhVEOot0sUHfwYui-E5XpmWWRC-msfqnxkcLdE0OCLDyGY2OXMC2P6vdt-HGXGWkJNdjjkFZdOXG79WSnoTor_ETo9cEYUAZ6LX83IWR8A \ No newline at end of file +eyJhbGciOiJSUzI1NiIsImtpZCI6ImFWODBkazlpZG1sbU1YVlBkMUJYV2xGcGIwZFdZVnBHYkRkVVYxSlFWWGRoV0cxVU9HeFNaRkZCYXcifQ.eyJyZXNwb25zZV90eXBlIjogImNvZGUiLCAic3RhdGUiOiAic3RhdGUiLCAicmVkaXJlY3RfdXJpIjogImh0dHBzOi8vZXhhbXBsZS5jb20vY2xpL2F1dGh6X2NiIiwgInNjb3BlIjogIm9wZW5pZCIsICJub25jZSI6ICJyRHlHU2wySFlKOGJaVTZGMWFoRmZoNk5wcUFwS0J3TSIsICJjbGllbnRfaWQiOiAiY2xpZW50X2lkIiwgImlzcyI6ICJjbGllbnRfaWQiLCAiaWF0IjogMTYxNjQ4OTM1OSwgImF1ZCI6IFsiaHR0cHM6Ly9leGFtcGxlLmNvbSJdfQ.H4U0f-xpMnWW79hclzs4KFdSX8lBvMiAxSdUqjIaURNsgScedJDuzVE-Qt7sSAFYvmWYPLOg6gdEoQdnZr168lnedf4YUHVz8N9b4YNl2Eh1aaaYb98mqcCEaqc30NW32FWONFAGtvK4csWpaAj4oPFCARzAxJNllc8ovRq_tVpydtyiEcSJarlXO6cHaLZefyNLL8Kqc3V3kT4pQi8eXsuSFNgZDHDvUleuBG9bUcbXkYZQr2c8bLIvazUNXI--VyaSZptef6StDOMxGfG3K9oNN7hMG2U6XM3p8qyYuWF7jWsJngkE3pilUrtqdNUs2XoC5s185E-Ai7sdsOLUJA \ No newline at end of file diff --git a/tests/test_01_service_context.py b/tests/test_01_service_context.py index 32428db..9d26100 100644 --- a/tests/test_01_service_context.py +++ b/tests/test_01_service_context.py @@ -7,6 +7,7 @@ from oidcservice.service_context import ServiceContext +BASE_URL = "https://entity.example.org" def test_client_info_init(): config = { @@ -15,12 +16,15 @@ def test_client_info_init(): 'base_url': 'https://example.com', 'requests_dir': 'requests' } - ci = ServiceContext(config=config) + ci = ServiceContext(BASE_URL, config=config) + + srvcnx = ServiceContext().load(ci.dump()) + for attr in config.keys(): try: - val = getattr(ci, attr) + val = getattr(srvcnx, attr) except AttributeError: - val = ci.get(attr) + val = srvcnx.get(attr) assert val == config[attr] diff --git a/tests/test_01_service_context_impexp.py b/tests/test_01_service_context_impexp.py new file mode 100644 index 0000000..9fda094 --- /dev/null +++ b/tests/test_01_service_context_impexp.py @@ -0,0 +1,282 @@ +import os +from urllib.parse import urlsplit + +from cryptojwt.key_jar import build_keyjar +import pytest +import responses + +from oidcservice.service_context import ServiceContext + + +def test_client_info_init(): + config = { + 'client_id': 'client_id', 'issuer': 'issuer', + 'client_secret': 'client_secret_wordplay', + 'base_url': 'https://example.com', + 'requests_dir': 'requests' + } + ci = ServiceContext(config=config) + + srvcnx = ServiceContext().load(ci.dump()) + + for attr in config.keys(): + try: + val = getattr(srvcnx, attr) + except AttributeError: + val = srvcnx.get(attr) + + assert val == config[attr] + + +def test_set_and_get_client_secret(): + service_context = ServiceContext() + service_context.client_secret = 'longenoughsupersecret' + + srvcnx2 = ServiceContext().load(service_context.dump()) + + assert srvcnx2.client_secret == 'longenoughsupersecret' + + +def test_set_and_get_client_id(): + service_context = ServiceContext() + service_context.client_id = 'myself' + srvcnx2 = ServiceContext().load(service_context.dump()) + assert srvcnx2.client_id == 'myself' + + +def test_client_filename(): + config = { + 'client_id': 'client_id', 'issuer': 'issuer', + 'client_secret': 'longenoughsupersecret', 'base_url': 'https://example.com', + 'requests_dir': 'requests' + } + service_context = ServiceContext(config=config) + srvcnx2 = ServiceContext().load(service_context.dump()) + fname = srvcnx2.filename_from_webname('https://example.com/rq12345') + assert fname == 'rq12345' + + +def verify_alg_support(service_context, alg, usage, typ): + """ + Verifies that the algorithm to be used are supported by the other side. + This will look at provider information either statically configured or + obtained through dynamic provider info discovery. + + :param alg: The algorithm specification + :param usage: In which context the 'alg' will be used. + The following contexts are supported: + - userinfo + - id_token + - request_object + - token_endpoint_auth + :param typ: Type of algorithm + - signing_alg + - encryption_alg + - encryption_enc + :return: True or False + """ + + supported = service_context.get('provider_info')[ + "{}_{}_values_supported".format(usage, typ)] + + if alg in supported: + return True + else: + return False + + +class TestClientInfo(object): + @pytest.fixture(autouse=True) + def create_client_info_instance(self): + config = { + 'client_id': 'client_id', 'issuer': 'issuer', + 'client_secret': 'longenoughsupersecret', + 'base_url': 'https://example.com', + 'requests_dir': 'requests' + } + self.service_context = ServiceContext(config=config) + + def test_registration_userinfo_sign_enc_algs(self): + self.service_context.set( + 'behaviour', { + "application_type": "web", + "redirect_uris": ["https://client.example.org/callback", + "https://client.example.org/callback2"], + "token_endpoint_auth_method": "client_secret_basic", + "jwks_uri": "https://client.example.org/my_public_keys.jwks", + "userinfo_encrypted_response_alg": "RSA1_5", + "userinfo_encrypted_response_enc": "A128CBC-HS256" + }) + + srvcntx = ServiceContext().load( + self.service_context.dump(exclude_attributes=["service_context"])) + assert srvcntx.get_sign_alg('userinfo') is None + assert srvcntx.get_enc_alg_enc('userinfo') == {'alg': 'RSA1_5', 'enc': 'A128CBC-HS256'} + + def test_registration_request_object_sign_enc_algs(self): + self.service_context.set('behaviour', { + "application_type": "web", + "redirect_uris": ["https://client.example.org/callback", + "https://client.example.org/callback2"], + "token_endpoint_auth_method": "client_secret_basic", + "jwks_uri": "https://client.example.org/my_public_keys.jwks", + "userinfo_encrypted_response_alg": "RSA1_5", + "userinfo_encrypted_response_enc": "A128CBC-HS256", + "request_object_signing_alg": "RS384" + }) + + srvcntx = ServiceContext().load( + self.service_context.dump(exclude_attributes=["service_context"])) + res = srvcntx.get_enc_alg_enc('userinfo') + # 'sign':'RS256' is an added default + assert res == {'alg': 'RSA1_5', 'enc': 'A128CBC-HS256'} + assert srvcntx.get_sign_alg('request_object') == 'RS384' + + def test_registration_id_token_sign_enc_algs(self): + self.service_context.set('behaviour', { + "application_type": "web", + "redirect_uris": ["https://client.example.org/callback", + "https://client.example.org/callback2"], + "token_endpoint_auth_method": "client_secret_basic", + "jwks_uri": "https://client.example.org/my_public_keys.jwks", + "userinfo_encrypted_response_alg": "RSA1_5", + "userinfo_encrypted_response_enc": "A128CBC-HS256", + "request_object_signing_alg": "RS384", + 'id_token_encrypted_response_alg': 'ECDH-ES', + 'id_token_encrypted_response_enc': "A128GCM", + 'id_token_signed_response_alg': "ES384", + }) + + srvcntx = ServiceContext().load( + self.service_context.dump(exclude_attributes=["service_context"])) + + # 'sign':'RS256' is an added default + assert srvcntx.get_enc_alg_enc('userinfo') == {'alg': 'RSA1_5', 'enc': 'A128CBC-HS256'} + assert srvcntx.get_sign_alg('request_object') == 'RS384' + assert srvcntx.get_enc_alg_enc('id_token') == {'alg': 'ECDH-ES', 'enc': 'A128GCM'} + + def test_verify_alg_support(self): + self.service_context.set('provider_info', { + "version": "3.0", + "issuer": "https://server.example.com", + "authorization_endpoint": + "https://server.example.com/connect/authorize", + "token_endpoint": "https://server.example.com/connect/token", + "token_endpoint_auth_methods_supported": ["client_secret_basic", + "private_key_jwt"], + "token_endpoint_auth_signing_alg_values_supported": ["RS256", + "ES256"], + "userinfo_endpoint": "https://server.example.com/connect/userinfo", + "check_session_iframe": + "https://server.example.com/connect/check_session", + "end_session_endpoint": + "https://server.example.com/connect/end_session", + "jwks_uri": "https://server.example.com/jwks.json", + "registration_endpoint": + "https://server.example.com/connect/register", + "scopes_supported": ["openid", "profile", "email", "address", + "phone", "offline_access"], + "response_types_supported": ["code", "code id_token", "id_token", + "token id_token"], + "acr_values_supported": ["urn:mace:incommon:iap:silver", + "urn:mace:incommon:iap:bronze"], + "subject_types_supported": ["public", "pairwise"], + "userinfo_signing_alg_values_supported": ["RS256", "ES256", + "HS256"], + "userinfo_encryption_alg_values_supported": ["RSA1_5", "A128KW"], + "userinfo_encryption_enc_values_supported": ["A128CBC+HS256", + "A128GCM"], + "id_token_signing_alg_values_supported": ["RS256", "ES256", + "HS256"], + "id_token_encryption_alg_values_supported": ["RSA1_5", "A128KW"], + "id_token_encryption_enc_values_supported": ["A128CBC+HS256", + "A128GCM"], + "request_object_signing_alg_values_supported": ["none", "RS256", + "ES256"], + "display_values_supported": ["page", "popup"], + "claim_types_supported": ["normal", "distributed"], + "claims_supported": ["sub", "iss", "auth_time", "acr", "name", + "given_name", "family_name", "nickname", + "profile", + "picture", "website", "email", + "email_verified", + "locale", "zoneinfo", + "http://example.info/claims/groups"], + "claims_parameter_supported": True, + "service_documentation": + "http://server.example.com/connect/service_documentation.html", + "ui_locales_supported": ["en-US", "en-GB", "en-CA", "fr-FR", + "fr-CA"] + }) + + srvcntx = ServiceContext().load( + self.service_context.dump(exclude_attributes=["service_context"])) + + assert verify_alg_support(srvcntx, 'RS256', 'id_token', 'signing_alg') + assert verify_alg_support(srvcntx, 'RS512', 'id_token', 'signing_alg') is False + assert verify_alg_support(srvcntx, 'RSA1_5', 'userinfo', 'encryption_alg') + + # token_endpoint_auth_signing_alg_values_supported + assert verify_alg_support(srvcntx, 'ES256', 'token_endpoint_auth', 'signing_alg') + + def test_verify_requests_uri(self): + self.service_context.set('provider_info', {'issuer': 'https://example.com/'}) + url_list = self.service_context.generate_request_uris('/leading') + sp = urlsplit(url_list[0]) + p = sp.path.split('/') + assert p[0] == '' + assert p[1] == 'leading' + assert len(p) == 3 + + srvcntx = ServiceContext().load( + self.service_context.dump(exclude_attributes=["service_context"])) + + # different for different OPs + srvcntx.set('provider_info', {'issuer': 'https://op.example.org/'}) + url_list = srvcntx.generate_request_uris('/leading') + sp = urlsplit(url_list[0]) + np = sp.path.split('/') + assert np[0] == '' + assert np[1] == 'leading' + assert len(np) == 3 + + assert np[2] != p[2] + + def test_import_keys_file(self): + # Should only be one and that a symmetric key (client_secret) usable + # for signing and encryption + assert len(self.service_context.keyjar.get_issuer_keys('')) == 1 + + file_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), 'salesforce.key')) + + keyspec = {'file': {'rsa': [file_path]}} + self.service_context.import_keys(keyspec) + + srvcntx = ServiceContext().load( + self.service_context.dump(exclude_attributes=["service_context"])) + + # Now there should be 2, the second a RSA key for signing + assert len(srvcntx.keyjar.get_issuer_keys('')) == 2 + + def test_import_keys_url(self): + assert len(self.service_context.keyjar.get_issuer_keys('')) == 1 + + # One EC key for signing + key_def = [{"type": "EC", "crv": "P-256", "use": ["sig"]}] + + keyjar = build_keyjar(key_def) + + with responses.RequestsMock() as rsps: + _jwks_url = 'https://foobar.com/jwks.json' + rsps.add("GET", _jwks_url, body=keyjar.export_jwks_as_json(), status=200, + adding_headers={"Content-Type": "application/json"}) + keyspec = {'url': {'https://foobar.com': _jwks_url}} + self.service_context.import_keys(keyspec) + self.service_context.keyjar.update() + + srvcntx = ServiceContext().load( + self.service_context.dump(exclude_attributes=["service_context"])) + + # Now there should be one belonging to https://example.com + assert len(srvcntx.keyjar.get_issuer_keys('https://foobar.com')) == 1 diff --git a/tests/test_07_service.py b/tests/test_07_service.py index 837645a..608a99a 100644 --- a/tests/test_07_service.py +++ b/tests/test_07_service.py @@ -51,7 +51,7 @@ def test_get_request_parameters(self): req_args = {'foo': 'bar', 'req_str': 'some string'} self.service.endpoint = 'https://example.com/authorize' _info = self.service.get_request_parameters(request_args=req_args) - assert set(_info.keys()) == {'url', 'method'} + assert set(_info.keys()) == {'url', 'method', "request"} msg = DummyMessage().from_urlencoded( self.service.get_urlinfo(_info['url'])) @@ -59,7 +59,7 @@ def test_request_init(self): req_args = {'foo': 'bar', 'req_str': 'some string'} self.service.endpoint = 'https://example.com/authorize' _info = self.service.get_request_parameters(request_args=req_args) - assert set(_info.keys()) == {'url', 'method'} + assert set(_info.keys()) == {'url', 'method', "request"} msg = DummyMessage().from_urlencoded( self.service.get_urlinfo(_info['url'])) assert msg.to_dict() == {'foo': 'bar', 'req_str': 'some string'} diff --git a/tests/test_07_service_impexp.py b/tests/test_07_service_impexp.py new file mode 100644 index 0000000..02f2bd4 --- /dev/null +++ b/tests/test_07_service_impexp.py @@ -0,0 +1,72 @@ +import pytest +from oidcmsg.oauth2 import (SINGLE_OPTIONAL_INT, SINGLE_OPTIONAL_STRING, + SINGLE_REQUIRED_STRING, Message) + +from oidcservice.service import Service +from oidcservice.service_context import ServiceContext +from oidcservice.state_interface import InMemoryStateDataBase, State + + +class DummyMessage(Message): + c_param = { + "req_str": SINGLE_REQUIRED_STRING, + "opt_str": SINGLE_OPTIONAL_STRING, + "opt_int": SINGLE_OPTIONAL_INT, + } + + +class Response(object): + def __init__(self, status_code, text, headers=None): + self.status_code = status_code + self.text = text + self.headers = headers or {"content-type": "text/plain"} + + +class DummyService(Service): + msg_type = DummyMessage + + +class TestDummyService(object): + @pytest.fixture(autouse=True) + def create_service(self): + service_context = ServiceContext(client_id='client_id', + issuer='https://www.example.org/as') + self.service = DummyService(service_context) + + def test_construct(self): + _srv = Service(self.service.dump(exclude_attributes=["service"])) + req_args = {'foo': 'bar'} + _req = _srv.construct(request_args=req_args) + assert isinstance(_req, Message) + assert list(_req.keys()) == ['foo'] + + def test_construct_service_context(self): + _srv = Service(self.service.dump(exclude_attributes=["service"])) + req_args = {'foo': 'bar', 'req_str': 'some string'} + _req = _srv.construct(request_args=req_args) + assert isinstance(_req, Message) + assert set(_req.keys()) == {'foo', 'req_str'} + + def test_get_request_parameters(self): + _srv = Service(self.service.dump(exclude_attributes=["service"])) + req_args = {'foo': 'bar', 'req_str': 'some string'} + _srv.endpoint = 'https://example.com/authorize' + _info = _srv.get_request_parameters(request_args=req_args) + assert set(_info.keys()) == {'url', 'method', "request"} + msg = DummyMessage().from_urlencoded(_srv.get_urlinfo(_info['url'])) + assert msg.to_dict() == {'foo': 'bar', 'req_str': 'some string'} + + +class TestRequest(object): + @pytest.fixture(autouse=True) + def create_service(self): + service_context = ServiceContext(None) + self.service = Service(service_context, client_authn_method=None) + + def test_construct(self): + _srv = Service(self.service.dump(exclude_attributes=["service"])) + + req_args = {'foo': 'bar'} + _req = _srv.construct(request_args=req_args) + assert isinstance(_req, Message) + assert list(_req.keys()) == ['foo'] diff --git a/tests/test_09_client_auth.py b/tests/test_09_client_auth.py index c2a40f7..e1d99db 100755 --- a/tests/test_09_client_auth.py +++ b/tests/test_09_client_auth.py @@ -52,14 +52,14 @@ def get_service(): @pytest.fixture def services(): - db = InMemoryStateDataBase() - auth_request = AuthorizationRequest(redirect_uri="http://example.com", - state='ABCDE').to_json() - auth_response = AuthorizationResponse(access_token="token", - state='ABCDE').to_json() - db.set('ABCDE', State(iss='Issuer', auth_request=auth_request, - auth_response=auth_response).to_json()) - return init_services(DEFAULT_SERVICES, get_service_context(), db) + # auth_request = AuthorizationRequest(redirect_uri="http://example.com", + # state='ABCDE').to_json() + # auth_response = AuthorizationResponse(access_token="token", + # state='ABCDE').to_json() + services = init_services(DEFAULT_SERVICES, get_service_context()) + # db.set('ABCDE', State(iss='Issuer', auth_request=auth_request, + # auth_response=auth_response).to_json()) + return services def test_quote(): @@ -147,11 +147,12 @@ def test_construct_with_resource_request(self): def test_construct_with_token(self, services): authz_service = services['authorization'] - _state = authz_service.create_state('Issuer') + srv_cntx = authz_service.service_context + _state = srv_cntx.state.create_state('Issuer') req = AuthorizationRequest(state=_state, response_type='code', redirect_uri='https://example.com', scope=['openid']) - authz_service.store_item(req, 'auth_request', _state) + srv_cntx.state.store_item(req, 'auth_request', _state) # Add a state and bind a code to it resp1 = AuthorizationResponse(code="auth_grant", state=_state) @@ -186,40 +187,43 @@ def test_construct(self, services): def test_construct_with_state(self, services): _srv = services['authorization'] - _srv.state_db['FFFFF'] = State(iss='Issuer').to_json() + _cntx = _srv.service_context + _key = _cntx.state.create_state(iss='Issuer') - resp = AuthorizationResponse(code="code", state="FFFFF") - _srv.store_item(resp, 'auth_response', 'FFFFF') + resp = AuthorizationResponse(code="code", state=_key) + _cntx.state.store_item(resp, 'auth_response', _key) atr = AccessTokenResponse(access_token="2YotnFZFEjr1zCsicMWpAA", token_type="example", refresh_token="tGzv3JOkF0XG5Qx2TlKWIA", example_parameter="example_value", scope=["inner", "outer"]) - _srv.store_item(atr, 'token_response', 'FFFFF') + _cntx.state.store_item(atr, 'token_response', _key) request = ResourceRequest() - http_args = BearerBody().construct(request, service=_srv, key="FFFFF") + http_args = BearerBody().construct(request, service=_srv, key=_key) assert request["access_token"] == "2YotnFZFEjr1zCsicMWpAA" assert http_args is None def test_construct_with_request(self, services): authz_service = services['authorization'] - authz_service.service_context.state_db['EEEE'] = State(iss='Issuer').to_json() - resp1 = AuthorizationResponse(code="auth_grant", state="EEEE") + _cntx = authz_service.service_context + + _key = _cntx.state.create_state(iss='Issuer') + resp1 = AuthorizationResponse(code="auth_grant", state=_key) response = authz_service.parse_response(resp1.to_urlencoded(), "urlencoded") - authz_service.update_service_context(response, key='EEEE') + authz_service.update_service_context(response, key=_key) resp2 = AccessTokenResponse(access_token="token1", token_type="Bearer", expires_in=0, - state="EEEE") + state=_key) response = services['accesstoken'].parse_response( resp2.to_urlencoded(), "urlencoded") - services['accesstoken'].update_service_context(response, key='EEEE') + services['accesstoken'].update_service_context(response, key=_key) request = ResourceRequest() - BearerBody().construct(request, service=authz_service, key="EEEE") + BearerBody().construct(request, service=authz_service, key=_key) assert "access_token" in request assert request["access_token"] == "token1" diff --git a/tests/test_10_oauth2_service.py b/tests/test_10_oauth2_service.py index fc726d2..85ced1c 100644 --- a/tests/test_10_oauth2_service.py +++ b/tests/test_10_oauth2_service.py @@ -40,8 +40,8 @@ def test_construct(self): _req = self.service.construct(request_args=req_args, state='state') assert isinstance(_req, AuthorizationRequest) assert set(_req.keys()) == {'client_id', 'redirect_uri', 'foo', 'state'} - assert self.service.service_context.state_db.get('state') - _item = self.service.get_item(AuthorizationRequest, 'auth_request', + assert self.service.service_context.state.get_state('state') + _item = self.service.service_context.state.get_item(AuthorizationRequest, 'auth_request', 'state') assert _item.to_dict() == { 'foo': 'bar', 'redirect_uri': 'https://example.com/cli/authz_cb', @@ -53,7 +53,7 @@ def test_get_request_parameters(self): self.service.endpoint = 'https://example.com/authorize' _info = self.service.get_request_parameters(request_args=req_args, state='state') - assert set(_info.keys()) == {'url', 'method'} + assert set(_info.keys()) == {'url', 'method', 'request'} msg = AuthorizationRequest().from_urlencoded( self.service.get_urlinfo(_info['url'])) assert msg.to_dict() == { @@ -66,7 +66,7 @@ def test_request_init(self): req_args = {'response_type': 'code', 'state': 'state'} self.service.endpoint = 'https://example.com/authorize' _info = self.service.get_request_parameters(request_args=req_args) - assert set(_info.keys()) == {'url', 'method'} + assert set(_info.keys()) == {'url', 'method', 'request'} msg = AuthorizationRequest().from_urlencoded( self.service.get_urlinfo(_info['url'])) assert msg.to_dict() == { @@ -92,8 +92,9 @@ def create_service(self): state='state' ) auth_response = AuthorizationResponse(code='access_code') - self.service.store_item(auth_request, 'auth_request', 'state') - self.service.store_item(auth_response, 'auth_response', 'state') + _state = self.service.service_context.state + _state.store_item(auth_request, 'auth_request', 'state') + _state.store_item(auth_response, 'auth_response', 'state') def test_construct(self): req_args = {'foo': 'bar', 'state': 'state'} @@ -125,7 +126,7 @@ def test_get_request_parameters(self): _info = self.service.get_request_parameters( request_args=req_args, state='state', authn_method='client_secret_basic') - assert set(_info.keys()) == {'headers', 'body', 'url', 'method'} + assert set(_info.keys()) == {'headers', 'body', 'url', 'method', 'request'} assert _info['url'] == 'https://example.com/authorize' assert 'Authorization' in _info['headers'] msg = AccessTokenRequest().from_urlencoded( @@ -146,7 +147,7 @@ def test_request_init(self): _info = self.service.get_request_parameters(request_args=req_args, state='state') - assert set(_info.keys()) == {'body', 'url', 'headers', 'method'} + assert set(_info.keys()) == {'body', 'url', 'headers', 'method', 'request'} assert _info['url'] == 'https://example.com/authorize' msg = AccessTokenRequest().from_urlencoded( self.service.get_urlinfo(_info['body'])) @@ -209,8 +210,9 @@ def create_service(self): auth_response = AuthorizationResponse(code='access_code') token_response = AccessTokenResponse(access_token='bearer_token', refresh_token='refresh') - self.service.store_item(auth_response, 'auth_response', 'abcdef') - self.service.store_item(token_response, 'token_response', 'abcdef') + _state = self.service.service_context.state + _state.store_item(auth_response, 'auth_response', 'abcdef') + _state.store_item(token_response, 'token_response', 'abcdef') self.service.endpoint = 'https://example.com/token' def test_construct(self): @@ -222,7 +224,7 @@ def test_construct(self): def test_get_request_parameters(self): _info = self.service.get_request_parameters(state='abcdef') - assert set(_info.keys()) == {'url', 'body', 'headers', 'method'} + assert set(_info.keys()) == {'url', 'body', 'headers', 'method', 'request'} def test_access_token_srv_conf(): @@ -239,8 +241,9 @@ def test_access_token_srv_conf(): auth_request = AuthorizationRequest( redirect_uri='https://example.com/cli/authz_cb', state='state') auth_response = AuthorizationResponse(code='access_code') - service.store_item(auth_request, "auth_request", 'state') - service.store_item(auth_response, "auth_response", 'state') + _state = service.service_context.state + _state.store_item(auth_request, "auth_request", 'state') + _state.store_item(auth_response, "auth_response", 'state') req_args = { 'redirect_uri': 'https://example.com/cli/authz_cb', diff --git a/tests/test_13_oic_service.py b/tests/test_13_oic_service.py index 7094cea..eba8e4e 100644 --- a/tests/test_13_oic_service.py +++ b/tests/test_13_oic_service.py @@ -1,23 +1,29 @@ import json import os -import pytest from cryptojwt.exception import UnsupportedAlgorithm from cryptojwt.jws import jws from cryptojwt.jws.utils import left_hash from cryptojwt.jwt import JWT -from cryptojwt.key_jar import build_keyjar, init_key_jar -from oidcmsg.oauth2 import (AccessTokenRequest, AccessTokenResponse, - AuthorizationRequest, AuthorizationResponse, - Message) -from oidcmsg.oidc import (IdToken, OpenIDSchema, RegistrationRequest, - verified_claim_name) -from oidcmsg.oidc.session import (CheckIDRequest, CheckSessionRequest, - EndSessionRequest) +from cryptojwt.key_jar import build_keyjar +from cryptojwt.key_jar import init_key_jar +from oidcmsg.oauth2 import AccessTokenRequest +from oidcmsg.oauth2 import AccessTokenResponse +from oidcmsg.oauth2 import AuthorizationRequest +from oidcmsg.oauth2 import AuthorizationResponse +from oidcmsg.oauth2 import Message +from oidcmsg.oidc import IdToken +from oidcmsg.oidc import OpenIDSchema +from oidcmsg.oidc import RegistrationRequest +from oidcmsg.oidc import verified_claim_name +from oidcmsg.oidc.session import CheckIDRequest +from oidcmsg.oidc.session import CheckSessionRequest +from oidcmsg.oidc.session import EndSessionRequest +import pytest from oidcservice.exception import ParameterError -from oidcservice.oidc.registration import (add_jwks_uri_or_jwks, - response_types_to_grant_types) +from oidcservice.oidc.registration import add_jwks_uri_or_jwks +from oidcservice.oidc.registration import response_types_to_grant_types from oidcservice.service_context import ServiceContext from oidcservice.service_factory import service_factory @@ -66,7 +72,7 @@ def create_request(self): 'client_id': 'client_id', 'client_secret': 'a longesh password', 'redirect_uris': ['https://example.com/cli/authz_cb'] } - service_context = ServiceContext(CLI_KEY, config=client_config) + service_context = ServiceContext(keyjar=CLI_KEY, config=client_config) service_context.set('issuer', 'https://example.com') self.service = service_factory('Authorization', ['oidc'], service_context=service_context) @@ -119,7 +125,7 @@ def test_get_request_parameters(self): req_args = {'response_type': 'code', 'state': 'state'} self.service.endpoint = 'https://example.com/authorize' _info = self.service.get_request_parameters(request_args=req_args) - assert set(_info.keys()) == {'url', 'method'} + assert set(_info.keys()) == {'url', 'method', 'request'} msg = AuthorizationRequest().from_urlencoded( self.service.get_urlinfo(_info['url'])) assert set(msg.keys()) == {'response_type', 'state', 'client_id', @@ -129,7 +135,7 @@ def test_request_init(self): req_args = {'response_type': 'code', 'state': 'state'} self.service.endpoint = 'https://example.com/authorize' _info = self.service.get_request_parameters(request_args=req_args) - assert set(_info.keys()) == {'url', 'method'} + assert set(_info.keys()) == {'url', 'method', 'request'} msg = AuthorizationRequest().from_urlencoded( self.service.get_urlinfo(_info['url'])) assert set(msg.keys()) == {'client_id', 'scope', 'response_type', @@ -140,7 +146,7 @@ def test_request_init_request_method(self): self.service.endpoint = 'https://example.com/authorize' _info = self.service.get_request_parameters(request_args=req_args, request_method='value') - assert set(_info.keys()) == {'url', 'method'} + assert set(_info.keys()) == {'url', 'method', 'request'} msg = AuthorizationRequest().from_urlencoded( self.service.get_urlinfo(_info['url'])) assert set(msg.to_dict()) == {'client_id', 'redirect_uri', 'request', @@ -169,7 +175,7 @@ def test_request_param(self): _info = self.service.get_request_parameters(request_args=req_args, request_method='reference') - assert set(_info.keys()) == {'url', 'method'} + assert set(_info.keys()) == {'url', 'method', 'request'} def test_update_service_context_no_idtoken(self): req_args = {'response_type': 'code', 'state': 'state'} @@ -319,10 +325,12 @@ def create_request(self): auth_request = AuthorizationRequest( redirect_uri='https://example.com/cli/authz_cb', state='state', response_type='code').to_json() - self.service.store_item(auth_request, "auth_request", 'state') + + _stat_interface = service_context.state + _stat_interface.store_item(auth_request, "auth_request", 'state') auth_response = AuthorizationResponse(code='access_code').to_json() - self.service.store_item(auth_response, "auth_response", 'state') + _stat_interface.store_item(auth_response, "auth_response", 'state') def test_construct(self): req_args = {'foo': 'bar'} @@ -343,7 +351,7 @@ def test_get_request_parameters(self): _info = self.service.get_request_parameters(request_args=req_args, state='state', authn_method='client_secret_basic') - assert set(_info.keys()) == {'body', 'url', 'headers', 'method'} + assert set(_info.keys()) == {'body', 'url', 'headers', 'method', 'request'} assert _info['url'] == 'https://example.com/authorize' msg = AccessTokenRequest().from_urlencoded( self.service.get_urlinfo(_info['body'])) @@ -362,7 +370,7 @@ def test_request_init(self): _info = self.service.get_request_parameters(request_args=req_args, state='state') - assert set(_info.keys()) == {'body', 'url', 'headers', 'method'} + assert set(_info.keys()) == {'body', 'url', 'headers', 'method', 'request'} assert _info['url'] == 'https://example.com/authorize' msg = AccessTokenRequest().from_urlencoded( self.service.get_urlinfo(_info['body'])) @@ -373,10 +381,11 @@ def test_request_init(self): } def test_id_token_nonce_match(self): - self.service.store_nonce2state('nonce', 'state') + _state_interface = self.service.service_context.state + _state_interface.store_nonce2state('nonce', 'state') resp = AccessTokenResponse() resp[verified_claim_name('id_token')] = {'nonce': 'nonce'} - self.service.store_nonce2state('nonce2', 'state2') + _state_interface.store_nonce2state('nonce2', 'state2') with pytest.raises(ParameterError): self.service.update_service_context(resp, key='state2') @@ -626,9 +635,10 @@ def create_request(self): self.service = service_factory('UserInfo', ['oidc'], service_context=service_context) + _state_interface = self.service.service_context.state # Add history auth_response = AuthorizationResponse(code='access_code').to_json() - self.service.store_item(auth_response, 'auth_response', 'abcde') + _state_interface.store_item(auth_response, 'auth_response', 'abcde') idtval = { 'nonce': 'KUEYfRM2VzKDaaKD', 'sub': 'diana', @@ -641,7 +651,7 @@ def create_request(self): token_response = AccessTokenResponse( access_token='access_token', id_token=idt, __verified_id_token=ver_idt).to_json() - self.service.store_item(token_response, 'token_response', 'abcde') + _state_interface.store_item(token_response, 'token_response', 'abcde') def test_construct(self): _req = self.service.construct(state='abcde') @@ -758,12 +768,14 @@ def create_request(self): service_context=service_context) def test_construct(self): - self.service.store_item(json.dumps({'id_token': 'a.signed.jwt'}), - 'token_response', - 'abcde') + _state_interface = self.service.service_context.state + _state_interface.store_item(json.dumps({'id_token': 'a.signed.jwt'}), + 'token_response', 'abcde') _req = self.service.construct(state='abcde') assert isinstance(_req, CheckSessionRequest) assert len(_req) == 1 + assert "id_token" in _req + assert _req["id_token"] == 'a.signed.jwt' class TestCheckID(object): @@ -781,11 +793,14 @@ def create_request(self): service_context=service_context) def test_construct(self): - self.service.store_item(json.dumps({'id_token': 'a.signed.jwt'}), - 'token_response', 'abcde') + _state_interface = self.service.service_context.state + _state_interface.store_item(json.dumps({'id_token': 'a.signed.jwt'}), + 'token_response', 'abcde') _req = self.service.construct(state='abcde') assert isinstance(_req, CheckIDRequest) assert len(_req) == 1 + assert "id_token" in _req + assert _req["id_token"] == 'a.signed.jwt' class TestEndSession(object): @@ -804,8 +819,8 @@ def create_request(self): service_context=service_context) def test_construct(self): - self.service.store_item(json.dumps({'id_token': 'a.signed.jwt'}), - 'token_response', 'abcde') + self.service.service_context.state.store_item(json.dumps({'id_token': 'a.signed.jwt'}), + 'token_response', 'abcde') _req = self.service.construct(state='abcde') assert isinstance(_req, EndSessionRequest) assert len(_req) == 3 diff --git a/tests/test_14_pkce.py b/tests/test_14_pkce.py index 2f864ee..c4c7e9c 100644 --- a/tests/test_14_pkce.py +++ b/tests/test_14_pkce.py @@ -70,24 +70,22 @@ def create_client(self): def test_add_code_challenge_default_values(self): auth_serv = self.service["authorization"] - _state = State(iss='Issuer') - auth_serv.service_context.state_db['state'] = _state.to_json() - request_args, _ = add_code_challenge({'state': 'state'}, auth_serv) + _state_key = auth_serv.service_context.state.create_state(iss="Issuer") + request_args, _ = add_code_challenge({'state': _state_key}, auth_serv) # default values are length:64 method:S256 assert set(request_args.keys()) == {'code_challenge', 'code_challenge_method', 'state'} assert request_args['code_challenge_method'] == 'S256' - request_args = add_code_verifier({}, auth_serv, state='state') + request_args = add_code_verifier({}, auth_serv, state=_state_key) assert len(request_args['code_verifier']) == 64 def test_authorization_and_pkce(self): auth_serv = self.service["authorization"] - _state = State(iss='Issuer') - auth_serv.service_context.state_db['state'] = _state.to_json() + _state = auth_serv.service_context.state.create_state(iss='Issuer') - request = auth_serv.construct_request({"state": 'state', "response_type": "code"}) + request = auth_serv.construct_request({"state": _state, "response_type": "code"}) assert set(request.keys()) == {'client_id', 'code_challenge', 'code_challenge_method', 'state', 'redirect_uri', 'response_type'} @@ -97,7 +95,7 @@ def test_access_token_and_pkce(self): request = authz_service.construct_request({"state": 'state', "response_type": "code"}) _state = request['state'] auth_response = AuthorizationResponse(code='access code') - authz_service.store_item(auth_response, 'auth_response', _state) + authz_service.service_context.state.store_item(auth_response, 'auth_response', _state) token_service = self.service["accesstoken"] request = token_service.construct_request(state=_state) diff --git a/tests/test_16_cc_oauth2_service.py b/tests/test_16_cc_oauth2_service.py index 9eb8716..6ca05d7 100644 --- a/tests/test_16_cc_oauth2_service.py +++ b/tests/test_16_cc_oauth2_service.py @@ -1,8 +1,9 @@ +from oidcmsg.oauth2 import AccessTokenResponse import pytest +from oidcservice import rndstr from oidcservice.service_context import ServiceContext from oidcservice.service_factory import service_factory -from oidcservice.state_interface import InMemoryStateDataBase KEYDEF = [{"type": "EC", "crv": "P-256", "use": ["sig"]}] @@ -39,6 +40,26 @@ def test_token_get_request(self): 'Content-Type': 'application/x-www-form-urlencoded' } + def test_token_parse_response(self): + request_args = {'grant_type': 'client_credentials'} + _srv = self.service['token'] + _request_info = _srv.get_request_parameters(request_args=request_args) + + response = AccessTokenResponse(**{ + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": 3600, + "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter": "example_value" + }) + + _response = _srv.parse_response(response.to_json(), sformat="json") + # since no state attribute is involved, a key is minted + _key = rndstr(16) + _srv.update_service_context(_response, key=_key) + info = _srv.service_context.state.get_item(AccessTokenResponse, 'token_response', _key) + assert '__expires_at' in info + def test_refresh_token_get_request(self): _srv = self.service['token'] _srv.update_service_context({ @@ -49,7 +70,8 @@ def test_refresh_token_get_request(self): "example_parameter": "example_value" }) _srv = self.service['refresh_token'] - _info = _srv.get_request_parameters() + _id = rndstr(16) + _info = _srv.get_request_parameters(state_id=_id) assert _info['method'] == 'POST' assert _info['url'] == 'https://example.com/token' assert _info[ @@ -58,3 +80,85 @@ def test_refresh_token_get_request(self): 'Authorization': 'Bearer tGzv3JOkF0XG5Qx2TlKWIA', 'Content-Type': 'application/x-www-form-urlencoded' } + + def test_refresh_token_parse_response(self): + request_args = {'grant_type': 'client_credentials'} + _srv = self.service['token'] + _request_info = _srv.get_request_parameters(request_args=request_args) + + response = AccessTokenResponse(**{ + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": 3600, + "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter": "example_value" + }) + + _response = _srv.parse_response(response.to_json(), sformat="json") + # since no state attribute is involved, a key is minted + _key = rndstr(16) + _srv.update_service_context(_response, key=_key) + info = _srv.service_context.state.get_item(AccessTokenResponse, 'token_response', _key) + assert '__expires_at' in info + + # Move from token to refresh token service + + _srv = self.service['refresh_token'] + _request_info = _srv.get_request_parameters(request_args=request_args, state=_key) + + refresh_response = AccessTokenResponse(**{ + "access_token": 'wy4R01DmMoB5xkI65nNkVv1l', + "token_type": "example", + "expires_in": 3600, + "refresh_token": 'lhNX9LSG8w1QuD6tSgc6CPfJ', + }) + + _response = _srv.parse_response(refresh_response.to_json(), sformat="json") + _srv.update_service_context(_response, key=_key) + info = _srv.service_context.state.get_item(AccessTokenResponse, 'token_response', _key) + assert '__expires_at' in info + + + def test_2nd_refresh_token_parse_response(self): + request_args = {'grant_type': 'client_credentials'} + _srv = self.service['token'] + _request_info = _srv.get_request_parameters(request_args=request_args) + + response = AccessTokenResponse(**{ + "access_token": "2YotnFZFEjr1zCsicMWpAA", + "token_type": "example", + "expires_in": 3600, + "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter": "example_value" + }) + + _response = _srv.parse_response(response.to_json(), sformat="json") + # since no state attribute is involved, a key is minted + _key = rndstr(16) + _srv.update_service_context(_response, key=_key) + info = _srv.service_context.state.get_item(AccessTokenResponse, 'token_response', _key) + assert '__expires_at' in info + + # Move from token to refresh token service + + _srv = self.service['refresh_token'] + _request_info = _srv.get_request_parameters(request_args=request_args, state=_key) + + refresh_response = AccessTokenResponse(**{ + "access_token": 'wy4R01DmMoB5xkI65nNkVv1l', + "token_type": "example", + "expires_in": 3600, + "refresh_token": 'lhNX9LSG8w1QuD6tSgc6CPfJ', + }) + + _response = _srv.parse_response(refresh_response.to_json(), sformat="json") + _srv.update_service_context(_response, key=_key) + info = _srv.service_context.state.get_item(AccessTokenResponse, 'token_response', _key) + assert '__expires_at' in info + + _request_info = _srv.get_request_parameters(request_args=request_args, state=_key) + assert _request_info['headers'] == { + 'Authorization': 'Bearer {}'.format(refresh_response["refresh_token"]), + 'Content-Type': 'application/x-www-form-urlencoded' + } + diff --git a/tests/test_20_conversation.py b/tests/test_20_conversation.py index a24ffc4..e5e8302 100644 --- a/tests/test_20_conversation.py +++ b/tests/test_20_conversation.py @@ -1,26 +1,29 @@ #!/usr/bin/env python3 import json import time -from urllib.parse import parse_qs, urlparse +from urllib.parse import parse_qs +from urllib.parse import urlparse from cryptojwt.jwt import JWT from cryptojwt.key_jar import KeyJar -from oidcmsg.oidc import (JRD, AccessTokenResponse, AuthorizationResponse, - Link, OpenIDSchema, ProviderConfigurationResponse, - RegistrationResponse) +from oidcmsg.oidc import AccessTokenResponse +from oidcmsg.oidc import AuthorizationResponse +from oidcmsg.oidc import JRD +from oidcmsg.oidc import Link +from oidcmsg.oidc import OpenIDSchema +from oidcmsg.oidc import ProviderConfigurationResponse from oidcservice.oidc import DEFAULT_SERVICES from oidcservice.oidc.webfinger import WebFinger from oidcservice.service import init_services from oidcservice.service_context import ServiceContext -from oidcservice.state_interface import InMemoryStateDataBase # ================== SETUP =========================== KEYSPEC = [ {"type": "RSA", "use": ["sig"]}, {"type": "EC", "crv": "P-256", "use": ["sig"]}, - ] +] JWKS_OP = { 'keys': [{ @@ -30,13 +33,18 @@ 'kid': 'c19uYlBJXzVfNjNZeGVnYmxncHZwUzZTZDVwUFdxdVJLU3AxQXdwaFdfbw', 'kty': 'RSA', 'n': '3ZblhNL2CjRktLM9vyDn8jnA4G1B1HCpPh' - '-gv2AK4m9qDBZPYZGOGqzeW3vanvLTBlqnPm0GHg4rOrfMEwwLrfMcgmg1y4GD0vVU8G9HP1-oUPtKUqaKOp313tFKzFh9_OHGQ6EmhxG7gegPR9kQXduTDXqBFi81MzRplIQ8DHLM3-n2CyDW1V-dhRVh-AM0ZcJyzR_DvZ3mhG44DysPdHQOSeWnpdn1d81-PriqZfhAF9tn1ihgtjXd5swf1HTSjLd7xv1hitGf2245Xmr-V2pQFzeMukLM3JKbTYbElsB7Zm0wZx49hZMtgx35XMoO04bifdbO3yLtTA5ovXN3fQ', + '-gv2AK4m9qDBZPYZGOGqzeW3vanvLTBlqnPm0GHg4rOrfMEwwLrfMcgmg1y4GD0vVU8G9HP1' + '-oUPtKUqaKOp313tFKzFh9_OHGQ6EmhxG7gegPR9kQXduTDXqBFi81MzRplIQ8DHLM3-n2CyDW1V-dhRVh' + '-AM0ZcJyzR_DvZ3mhG44DysPdHQOSeWnpdn1d81' + '-PriqZfhAF9tn1ihgtjXd5swf1HTSjLd7xv1hitGf2245Xmr' + '-V2pQFzeMukLM3JKbTYbElsB7Zm0wZx49hZMtgx35XMoO04bifdbO3yLtTA5ovXN3fQ', 'p': '88aNu59aBn0elksaVznzoVKkdbT5B4euhOIEqJoFvFbEocw9mC4k' - '-yozIAQSV5FEakoSPOl8lrymCoM3Q1fVHfaM9Rbb9RCRlsV1JOeVVZOE05HUdz8zOIqLBDEGM_oQqDwF_kp-4nDTZ1-dtnGdTo4Cf7QRuApzE_dwVabUCTc', + '-yozIAQSV5FEakoSPOl8lrymCoM3Q1fVHfaM9Rbb9RCRlsV1JOeVVZOE05HUdz8zOIqLBDEGM_oQqDwF_kp' + '-4nDTZ1-dtnGdTo4Cf7QRuApzE_dwVabUCTc', 'q': '6LOHuM7H_0kDrMTwUEX7Aubzr792GoJ6EgTKIQY25SAFTZpYwuC3NnqlAdy8foIa3d7eGU2yICRbBG0S_ITcooDFrOa7nZ6enMUclMTxW8FwwvBXeIHo9cIsrKYtOThGplz43Cvl73MK5M58ZRmuhaNYa6Mk4PL4UokARfEiDus', 'use': 'sig' - }, + }, { 'crv': 'P-256', 'd': 'N2dg0-DAROBF8owQA4-uY5s0Ab-Fep_42kEFQG4BNVQ', @@ -45,8 +53,8 @@ 'use': 'sig', 'x': 'Ls8SqX8Ti5QAKtw3rdGr5K537-tqQCIbhyebeE_2C38', 'y': 'S-BrbPQkh8HVFLWg5Wid_5OAk4ewn5skHlHtG08ShaA' - } - ] + } + ] } OP_KEYJAR = KeyJar() @@ -68,7 +76,8 @@ "p": "_STNoJFkX9_uw8whytVmTrHP5K7vcZBIH9nuCTvj137lC48ZpR1UARx4qShxHLfK7DrufHd7TYnJkEMNUHFmdKvkaVQMY0_BsBSvCrUl10gzxsI08hg53L17E1Pe73iZp3f5nA4eB-1YB-km1Cc-Xs10OPWedJHf9brlCPDLAb8", "q": - "yz9T0rPEc0ZPjSi45gsYiQL2KJ3UsPHmLrgOHq0D4UvsB6UFtUtOWh7A1UpQdmBuHjIJz-Iq7VH4kzlI6VxoXhwE69oxBXr4I7fBudZRvlLuIJS9M2wvsTVouj0DBYSR6ZlAQHCCou89P2P6zQCEaqu7bWXNcpyTixbbvOU1w9k" + "yz9T0rPEc0ZPjSi45gsYiQL2KJ3UsPHmLrgOHq0D4UvsB6UFtUtOWh7A1UpQdmBuHjIJz" + "-Iq7VH4kzlI6VxoXhwE69oxBXr4I7fBudZRvlLuIJS9M2wvsTVouj0DBYSR6ZlAQHCCou89P2P6zQCEaqu7bWXNcpyTixbbvOU1w9k" }, { "kty": "EC", "use": "sig", @@ -78,7 +87,7 @@ "y": "EpxHNZp6ykyeLiS6r7l9ly2in1Zju7hnLk7RFraklxE", "d": "pepDloEcTyHnoEuqFirZ8hpt861piMDgiuvHIhhRSpM" }] - } +} RP_KEYJAR = KeyJar() RP_KEYJAR.import_jwks(RP_JWKS, '') @@ -93,8 +102,8 @@ def test_conversation(): service_context = ServiceContext( - RP_KEYJAR, - { + keyjar = RP_KEYJAR, + config = { "client_preferences": { "application_type": "web", @@ -103,17 +112,17 @@ def test_conversation(): "response_types": ["code"], "scope": ["openid", "profile", "email", "address", "phone"], "token_endpoint_auth_method": "client_secret_basic", - }, + }, "redirect_uris": ["{}/authz_cb".format(RP_BASEURL)], "jwks_uri": "{}/static/jwks.json".format(RP_BASEURL) - } - ) + } + ) service_spec = DEFAULT_SERVICES.copy() service_spec['WebFinger'] = {'class': WebFinger} service = init_services(service_spec, - service_context=service_context) + service_context=service_context) assert set(service.keys()) == {'accesstoken', 'authorization', 'webfinger', 'registration', 'refresh_token', 'userinfo', @@ -136,9 +145,9 @@ def test_conversation(): webfinger_response = json.dumps({ "subject": "acct:foobar@example.org", "links": [{ - "rel": "http://openid.net/specs/connect/1.0/issuer", - "href": "https://example.org/op" - }], + "rel": "http://openid.net/specs/connect/1.0/issuer", + "href": "https://example.org/op" + }], "expires": "2018-02-04T11:08:41Z" }) @@ -309,6 +318,8 @@ def test_conversation(): STATE = 'Oh3w3gKlvoM2ehFqlxI3HIK5' NONCE = 'UvudLKz287YByZdsY3AJoPAlEXQkJ0dK' + _state_interface = service["authorization"].service_context.state + info = service['authorization'].get_request_parameters( request_args={'state': STATE, 'nonce': NONCE}) @@ -332,8 +343,8 @@ def test_conversation(): _resp = service['authorization'].parse_response(_authz_rep.to_urlencoded()) service['authorization'].update_service_context(_resp, key=STATE) - _item = service['authorization'].get_item(AuthorizationResponse, - 'auth_response', STATE) + _item = _state_interface.get_item(AuthorizationResponse, + 'auth_response', STATE) assert _item['code'] == 'Z0FBQUFBQmFkdFFjUVpFWE81SHU5N1N4N01' # =================== Access token ==================== @@ -389,8 +400,8 @@ def test_conversation(): service['accesstoken'].update_service_context(_resp, key=STATE) - _item = service['authorization'].get_item(AccessTokenResponse, - 'token_response', STATE) + _item = _state_interface.get_item(AccessTokenResponse, + 'token_response', STATE) assert set(_item.keys()) == {'state', 'scope', 'access_token', 'token_type', 'id_token', @@ -415,6 +426,6 @@ def test_conversation(): assert isinstance(_resp, OpenIDSchema) assert _resp.to_dict() == {'sub': '1b2fc9341a16ae4e30082965d537'} - _item = service['authorization'].get_item(OpenIDSchema, - 'user_info', STATE) + _item = _state_interface.get_item(OpenIDSchema, + 'user_info', STATE) assert _item.to_dict() == {'sub': '1b2fc9341a16ae4e30082965d537'} diff --git a/tests/test_21_pushed_auth.py b/tests/test_21_pushed_auth.py index 2ecc2d1..ac64ea7 100644 --- a/tests/test_21_pushed_auth.py +++ b/tests/test_21_pushed_auth.py @@ -49,7 +49,7 @@ def create_client(self): } _cam = ca_factory _srvs = DEFAULT_SERVICES - service_context = ServiceContext(CLI_KEY, client_id='client_id', + service_context = ServiceContext(keyjar=CLI_KEY, client_id='client_id', issuer='https://www.example.org/as', config=config) diff --git a/tests/test_30_persistence.py b/tests/test_30_persistence.py deleted file mode 100644 index 891d98e..0000000 --- a/tests/test_30_persistence.py +++ /dev/null @@ -1,444 +0,0 @@ -#!/usr/bin/env python3 -import json -import shutil -import time -from urllib.parse import parse_qs, urlparse - -import responses -from cryptojwt.jwt import JWT -from cryptojwt.key_jar import KeyJar -from oidcmsg.oidc import (AccessTokenResponse, AuthorizationResponse, - OpenIDSchema) - -from oidcservice.oidc import DEFAULT_SERVICES -from oidcservice.oidc.webfinger import WebFinger -from oidcservice.service import init_services -from oidcservice.service_context import ServiceContext - -# ================== SETUP =========================== - -KEYSPEC = [ - {"type": "RSA", "use": ["sig"]}, - {"type": "EC", "crv": "P-256", "use": ["sig"]}, -] - -JWKS_OP = { - 'keys': [{ - 'd': 'mcAW1xeNsjzyV1M7F7_cUHz0MIR' - '-tcnKFJnbbo5UXxMRUPu17qwRHr8ttep1Ie64r2L9QlphcT9BjYd0KQ8ll3flIzLtiJv__MNPQVjk5bsYzb_erQRzSwLJU-aCcNFB8dIyQECzu-p44UVEPQUGzykImsSShvMQhcvrKiqqg7NlijJuEKHaKynV9voPsjwKYSqk6lH8kMloCaVS-dOkK-r7bZtbODUxx9GJWnxhX0JWXcdrPZRb29y9cdthrMcEaCXG23AxnMEfp-enDqarLHYTQrCBJXs_b-9k2d8v9zLm7E-Pf-0YGmaoJtX89lwQkO_SmFF3sXsnI2cFreqU3Q', - 'e': 'AQAB', - 'kid': 'c19uYlBJXzVfNjNZeGVnYmxncHZwUzZTZDVwUFdxdVJLU3AxQXdwaFdfbw', - 'kty': 'RSA', - 'n': '3ZblhNL2CjRktLM9vyDn8jnA4G1B1HCpPh' - '-gv2AK4m9qDBZPYZGOGqzeW3vanvLTBlqnPm0GHg4rOrfMEwwLrfMcgmg1y4GD0vVU8G9HP1' - '-oUPtKUqaKOp313tFKzFh9_OHGQ6EmhxG7gegPR9kQXduTDXqBFi81MzRplIQ8DHLM3-n2CyDW1V-dhRVh' - '-AM0ZcJyzR_DvZ3mhG44DysPdHQOSeWnpdn1d81' - '-PriqZfhAF9tn1ihgtjXd5swf1HTSjLd7xv1hitGf2245Xmr' - '-V2pQFzeMukLM3JKbTYbElsB7Zm0wZx49hZMtgx35XMoO04bifdbO3yLtTA5ovXN3fQ', - 'p': '88aNu59aBn0elksaVznzoVKkdbT5B4euhOIEqJoFvFbEocw9mC4k' - '-yozIAQSV5FEakoSPOl8lrymCoM3Q1fVHfaM9Rbb9RCRlsV1JOeVVZOE05HUdz8zOIqLBDEGM_oQqDwF_kp' - '-4nDTZ1-dtnGdTo4Cf7QRuApzE_dwVabUCTc', - 'q': - '6LOHuM7H_0kDrMTwUEX7Aubzr792GoJ6EgTKIQY25SAFTZpYwuC3NnqlAdy8foIa3d7eGU2yICRbBG0S_ITcooDFrOa7nZ6enMUclMTxW8FwwvBXeIHo9cIsrKYtOThGplz43Cvl73MK5M58ZRmuhaNYa6Mk4PL4UokARfEiDus', - 'use': 'sig' - }, - { - 'crv': 'P-256', - 'd': 'N2dg0-DAROBF8owQA4-uY5s0Ab-Fep_42kEFQG4BNVQ', - 'kid': 'UnpYbi0tWC1HaEtyRFMtSmkyZDVHUHZVNDF0d21KTVk1dzEwYmhpNlVtQQ', - 'kty': 'EC', - 'use': 'sig', - 'x': 'Ls8SqX8Ti5QAKtw3rdGr5K537-tqQCIbhyebeE_2C38', - 'y': 'S-BrbPQkh8HVFLWg5Wid_5OAk4ewn5skHlHtG08ShaA' - } - ] -} - -OP_KEYJAR = KeyJar() -OP_KEYJAR.import_jwks(JWKS_OP, '') -OP_BASEURL = "https://example.org" - -RP_JWKS = { - "keys": [{ - "kty": "RSA", "use": "sig", - "kid": "Mk0yN2w0N3BZLWtyOEpQWGFmNDZvQi1hbDl2azR3ai1WNElGdGZQSFd6MA", - "e": "AQAB", - "n": "yPrOADZtGoa9jxFCmDsJ1nAYmzgznUxCtUlb_ty33" - "-AFNEqzW_pSLr5g6RQAPGsvVQqbsb9AB18QNgz" - "-eG7cnvKIIR7JXWCuGv_Q9MwoRD0-zaYGRbRvFoTZokZMB6euBfMo6kijJ" - "-gdKuSaxIE84X_Fcf1ESAKJ0EX6Cxdm8hKkBelGIDPMW5z7EHQ8OuLCQtTJnDvbjEOk9sKzkKqVj53XFs5vjd4WUhxS6xIDcWE-lTafUpm0BsobklLePidHxyAMGOunL_Pt3RCLZGlWeWOO9fZhLtydiDWiZlcNR0FQEX_mfV1kCOHHBFN1VKOY2pyJpjp9djdtHxPZ9fP35w", - "d": - "aRBTqGDLYFaXuba4LYSPe_5Vnq8erFg1dzfGU9Fmfi5KCjAS2z5cv_reBnpiNTODJt3Izn7AJhpYCyl3zdWGl8EJ0OabNalY2txoi9A-LI4nyrHEDaRpfkgszVwaWtYZbxrShMc8I5x_wvCGx7sX7Hoy6YgQreRFzw8Fy86MDncpmcUwQTnXVUMLgioeYz5gW6rwXkqj_NVyuHPiheykJG026cXFNBWplCk4ET1bvf_6ZB9QmLwO16Pu2O-dtu1HHDOqI7y6-YgKIC6mcLrQrF9-FO7NkilcOB7zODNiYzhDBQ2YJAbcdn_3M_lkhaFwR-n4WB7vCM0vNqz7lEg6QQ", - "p": - "_STNoJFkX9_uw8whytVmTrHP5K7vcZBIH9nuCTvj137lC48ZpR1UARx4qShxHLfK7DrufHd7TYnJkEMNUHFmdKvkaVQMY0_BsBSvCrUl10gzxsI08hg53L17E1Pe73iZp3f5nA4eB-1YB-km1Cc-Xs10OPWedJHf9brlCPDLAb8", - "q": - "yz9T0rPEc0ZPjSi45gsYiQL2KJ3UsPHmLrgOHq0D4UvsB6UFtUtOWh7A1UpQdmBuHjIJz" - "-Iq7VH4kzlI6VxoXhwE69oxBXr4I7fBudZRvlLuIJS9M2wvsTVouj0DBYSR6ZlAQHCCou89P2P6zQCEaqu7bWXNcpyTixbbvOU1w9k" - }, { - "kty": "RSA", "use": "enc", - "kid": "Mk0yN2w0N3BZLWtyOEpQWGFmNDZvQi1hbDl2azR3ai1WNElGdGZQSFd6MA", - "e": "AQAB", - "n": "yPrOADZtGoa9jxFCmDsJ1nAYmzgznUxCtUlb_ty33" - "-AFNEqzW_pSLr5g6RQAPGsvVQqbsb9AB18QNgz" - "-eG7cnvKIIR7JXWCuGv_Q9MwoRD0-zaYGRbRvFoTZokZMB6euBfMo6kijJ" - "-gdKuSaxIE84X_Fcf1ESAKJ0EX6Cxdm8hKkBelGIDPMW5z7EHQ8OuLCQtTJnDvbjEOk9sKzkKqVj53XFs5vjd4WUhxS6xIDcWE-lTafUpm0BsobklLePidHxyAMGOunL_Pt3RCLZGlWeWOO9fZhLtydiDWiZlcNR0FQEX_mfV1kCOHHBFN1VKOY2pyJpjp9djdtHxPZ9fP35w", - "d": - "aRBTqGDLYFaXuba4LYSPe_5Vnq8erFg1dzfGU9Fmfi5KCjAS2z5cv_reBnpiNTODJt3Izn7AJhpYCyl3zdWGl8EJ0OabNalY2txoi9A-LI4nyrHEDaRpfkgszVwaWtYZbxrShMc8I5x_wvCGx7sX7Hoy6YgQreRFzw8Fy86MDncpmcUwQTnXVUMLgioeYz5gW6rwXkqj_NVyuHPiheykJG026cXFNBWplCk4ET1bvf_6ZB9QmLwO16Pu2O-dtu1HHDOqI7y6-YgKIC6mcLrQrF9-FO7NkilcOB7zODNiYzhDBQ2YJAbcdn_3M_lkhaFwR-n4WB7vCM0vNqz7lEg6QQ", - "p": - "_STNoJFkX9_uw8whytVmTrHP5K7vcZBIH9nuCTvj137lC48ZpR1UARx4qShxHLfK7DrufHd7TYnJkEMNUHFmdKvkaVQMY0_BsBSvCrUl10gzxsI08hg53L17E1Pe73iZp3f5nA4eB-1YB-km1Cc-Xs10OPWedJHf9brlCPDLAb8", - "q": - "yz9T0rPEc0ZPjSi45gsYiQL2KJ3UsPHmLrgOHq0D4UvsB6UFtUtOWh7A1UpQdmBuHjIJz" - "-Iq7VH4kzlI6VxoXhwE69oxBXr4I7fBudZRvlLuIJS9M2wvsTVouj0DBYSR6ZlAQHCCou89P2P6zQCEaqu7bWXNcpyTixbbvOU1w9k" - }, { - "kty": "EC", "use": "sig", - "kid": "ME9NV3VQV292OTA4T1pNLXZoVjd2TldVSjNrNEkycjU2ZjkycldQOTcyUQ", - "crv": "P-256", - "x": "WWoO_Exim-LOD1k8QPi_CdU8M_VUSF7DkJCKR7PFWhQ", - "y": "EpxHNZp6ykyeLiS6r7l9ly2in1Zju7hnLk7RFraklxE", - "d": "pepDloEcTyHnoEuqFirZ8hpt861piMDgiuvHIhhRSpM" - }] -} - -RP_KEYJAR = KeyJar() -RP_KEYJAR.import_jwks(RP_JWKS, '') -RP_BASEURL = "https://example.com/rp" - -SERVICE_PUBLIC_JWKS = RP_KEYJAR.export_jwks('') -OP_KEYJAR.import_jwks(SERVICE_PUBLIC_JWKS, RP_BASEURL) - - -# --------------------------------------------------- -def build_service_context() -> object: - _service_context = ServiceContext( - config={ - "issuer": OP_BASEURL, - "client_preferences": - { - "application_type": "web", - "application_name": "rphandler", - "contacts": ["ops@example.org"], - "response_types": ["code"], - "scope": ["openid", "profile", "email", "address", "phone"], - "token_endpoint_auth_method": "client_secret_basic", - }, - "redirect_uris": ["{}/authz_cb".format(RP_BASEURL)], - "jwks_uri": "{}/static/jwks.json".format(RP_BASEURL), - 'jwks': RP_JWKS, - 'db_conf': { - 'keyjar': { - 'handler': 'oidcmsg.storage.abfile.LabeledAbstractFileSystem', - 'fdir': 'db/{issuer}/keyjar', - 'key_conv': 'oidcmsg.storage.converter.QPKey', - 'value_conv': 'cryptojwt.serialize.item.KeyIssuer', - 'label': 'keyjar' - }, - 'default': { - 'handler': 'oidcmsg.storage.abfile.AbstractFileSystem', - 'fdir': 'db/{issuer}', - 'key_conv': 'oidcmsg.storage.converter.QPKey', - 'value_conv': 'oidcmsg.storage.converter.JSON' - }, - 'state': { - 'handler': 'oidcmsg.storage.abfile.AbstractFileSystem', - 'fdir': 'db/{issuer}/state', - 'key_conv': 'oidcmsg.storage.converter.QPKey', - 'value_conv': 'oidcmsg.storage.converter.JSON' - } - } - } - ) - - service_spec = DEFAULT_SERVICES.copy() - service_spec['WebFinger'] = {'class': WebFinger} - - _service = init_services(service_spec, service_context=_service_context) - - assert set(_service.keys()) == {'accesstoken', 'authorization', 'webfinger', - 'registration', 'refresh_token', 'userinfo', - 'provider_info'} - - _service_context.service = _service - return _service_context - - -def test_conversation(): - try: - shutil.rmtree('db') - except FileNotFoundError: - pass - - # Alternate who's doing what - service_context = build_service_context() - service_context_2 = build_service_context() - - service = service_context.service - service_2 = service_context_2.service - - # ======================== WebFinger ======================== - info = service['webfinger'].get_request_parameters( - request_args={'resource': 'foobar@example.org'}) - - _url = ('{}/.well-known/webfinger?rel=http' - '%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer' - '&resource=acct%3Afoobar%40example.org').format(OP_BASEURL) - assert info['url'] == _url - - webfinger_response = json.dumps({ - "subject": "acct:foobar@example.org", - "links": [{ - "rel": "http://openid.net/specs/connect/1.0/issuer", - "href": OP_BASEURL # "https://example.org/op" - }], - "expires": "2018-02-04T11:08:41Z" - }) - - response = service['webfinger'].parse_response(webfinger_response) - - service['webfinger'].update_service_context(resp=response) - - # =================== Provider info discovery ==================== - - info = service_context_2.service['provider_info'].get_request_parameters() - - assert info['url'] == '{}/.well-known/openid-configuration'.format(OP_BASEURL) - - provider_info_response = json.dumps({ - "version": "3.0", - "token_endpoint_auth_methods_supported": [ - "client_secret_post", "client_secret_basic", - "client_secret_jwt", "private_key_jwt"], - "claims_parameter_supported": True, - "request_parameter_supported": True, - "request_uri_parameter_supported": True, - "require_request_uri_registration": True, - "grant_types_supported": ["authorization_code", - "implicit", - "urn:ietf:params:oauth:grant-type:jwt-bearer", - "refresh_token"], - "response_types_supported": ["code", "id_token", - "id_token token", - "code id_token", - "code token", - "code id_token token"], - "response_modes_supported": ["query", "fragment", - "form_post"], - "subject_types_supported": ["public", "pairwise"], - "claim_types_supported": ["normal", "aggregated", - "distributed"], - "claims_supported": ["birthdate", "address", - "nickname", "picture", "website", - "email", "gender", "sub", - "phone_number_verified", - "given_name", "profile", - "phone_number", "updated_at", - "middle_name", "name", "locale", - "email_verified", - "preferred_username", "zoneinfo", - "family_name"], - "scopes_supported": ["openid", "profile", "email", - "address", "phone", - "offline_access", "openid"], - "userinfo_signing_alg_values_supported": [ - "RS256", "RS384", "RS512", - "ES256", "ES384", "ES512", - "HS256", "HS384", "HS512", - "PS256", "PS384", "PS512", "none"], - "id_token_signing_alg_values_supported": [ - "RS256", "RS384", "RS512", - "ES256", "ES384", "ES512", - "HS256", "HS384", "HS512", - "PS256", "PS384", "PS512", "none"], - "request_object_signing_alg_values_supported": [ - "RS256", "RS384", "RS512", "ES256", "ES384", - "ES512", "HS256", "HS384", "HS512", "PS256", - "PS384", "PS512", "none"], - "token_endpoint_auth_signing_alg_values_supported": [ - "RS256", "RS384", "RS512", "ES256", "ES384", - "ES512", "HS256", "HS384", "HS512", "PS256", - "PS384", "PS512"], - "userinfo_encryption_alg_values_supported": [ - "RSA1_5", "RSA-OAEP", "RSA-OAEP-256", - "A128KW", "A192KW", "A256KW", - "ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"], - "id_token_encryption_alg_values_supported": [ - "RSA1_5", "RSA-OAEP", "RSA-OAEP-256", - "A128KW", "A192KW", "A256KW", - "ECDH-ES", "ECDH-ES+A128KW", "ECDH-ES+A192KW", "ECDH-ES+A256KW"], - "request_object_encryption_alg_values_supported": [ - "RSA1_5", "RSA-OAEP", "RSA-OAEP-256", "A128KW", - "A192KW", "A256KW", "ECDH-ES", "ECDH-ES+A128KW", - "ECDH-ES+A192KW", "ECDH-ES+A256KW"], - "userinfo_encryption_enc_values_supported": [ - "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", - "A128GCM", "A192GCM", "A256GCM"], - "id_token_encryption_enc_values_supported": [ - "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", - "A128GCM", "A192GCM", "A256GCM"], - "request_object_encryption_enc_values_supported": [ - "A128CBC-HS256", "A192CBC-HS384", "A256CBC-HS512", - "A128GCM", "A192GCM", "A256GCM"], - "acr_values_supported": ["PASSWORD"], - "issuer": OP_BASEURL, - "jwks_uri": "{}/static/jwks_tE2iLbOAqXhe8bqh.json".format(OP_BASEURL), - "authorization_endpoint": "{}/authorization".format(OP_BASEURL), - "token_endpoint": "{}/token".format(OP_BASEURL), - "userinfo_endpoint": "{}/userinfo".format(OP_BASEURL), - "registration_endpoint": "{}/registration".format(OP_BASEURL), - "end_session_endpoint": "{}/end_session".format(OP_BASEURL) - }) - - resp = service_2['provider_info'].parse_response(provider_info_response) - - with responses.RequestsMock() as rsps: - _jwks_url = "{}/static/jwks_tE2iLbOAqXhe8bqh.json".format(OP_BASEURL) - rsps.add("GET", _jwks_url, body=OP_KEYJAR.export_jwks_as_json(), status=200, - adding_headers={"Content-Type": "application/json"}) - - service_2['provider_info'].update_service_context(resp) - - # =================== Client registration ==================== - - info = service['registration'].get_request_parameters() - - assert info['url'] == '{}/registration'.format(OP_BASEURL) - _body = json.loads(info['body']) - assert _body == { - "application_type": "web", - "response_types": ["code"], - "contacts": ["ops@example.org"], - "jwks_uri": "{}/static/jwks.json".format(RP_BASEURL), - "redirect_uris": ["{}/authz_cb".format(RP_BASEURL)], - 'token_endpoint_auth_method': 'client_secret_basic', - "grant_types": ["authorization_code"] - } - assert info['headers'] == {'Content-Type': 'application/json'} - - now = int(time.time()) - - op_client_registration_response = json.dumps({ - "client_id": "zls2qhN1jO6A", - "client_secret": "c8434f28cf9375d9a7", - "registration_access_token": "NdGrGR7LCuzNtixvBFnDphGXv7wRcONn", - "registration_client_uri": "{}/registration?client_id=zls2qhN1jO6A".format( - RP_BASEURL), - "client_secret_expires_at": now + 3600, - "client_id_issued_at": now, - "application_type": "web", - "response_types": ["code"], - "contacts": ["ops@example.com"], - "redirect_uris": ["{}/authz_cb".format(RP_BASEURL)], - "token_endpoint_auth_method": "client_secret_basic", - "grant_types": ["authorization_code"] - }) - - response = service['registration'].parse_response(op_client_registration_response) - - service['registration'].update_service_context(response) - - # =================== Authorization ==================== - - STATE = 'Oh3w3gKlvoM2ehFqlxI3HIK5' - NONCE = 'UvudLKz287YByZdsY3AJoPAlEXQkJ0dK' - - info = service_2['authorization'].get_request_parameters( - request_args={'state': STATE, 'nonce': NONCE}) - - p = urlparse(info['url']) - _query = parse_qs(p.query) - - op_authz_resp = { - 'state': STATE, - 'scope': 'openid', - 'code': 'Z0FBQUFBQmFkdFFjUVpFWE81SHU5N1N4N01', - 'iss': OP_BASEURL, - 'client_id': 'zls2qhN1jO6A' - } - - _authz_rep = AuthorizationResponse(**op_authz_resp) - - _resp = service_2['authorization'].parse_response(_authz_rep.to_urlencoded()) - service_2['authorization'].update_service_context(_resp, key=STATE) - - # _item = service['authorization'].get_item(AuthorizationResponse, - # 'auth_response', STATE) - - # =================== Access token ==================== - - request_args = { - 'state': STATE, - 'redirect_uri': service_context.get('redirect_uris')[0] - } - - info = service['accesstoken'].get_request_parameters( - request_args=request_args) - - assert info['url'] == '{}/token'.format(OP_BASEURL) - _qp = parse_qs(info['body']) - assert _qp == { - 'grant_type': ['authorization_code'], - 'redirect_uri': ['{}/authz_cb'.format(RP_BASEURL)], - 'client_id': ['zls2qhN1jO6A'], - 'state': ['Oh3w3gKlvoM2ehFqlxI3HIK5'], - 'code': ['Z0FBQUFBQmFkdFFjUVpFWE81SHU5N1N4N01'] - } - assert info['headers'] == { - 'Authorization': 'Basic ' - 'emxzMnFoTjFqTzZBOmM4NDM0ZjI4Y2Y5Mzc1ZDlhNw==', - 'Content-Type': 'application/x-www-form-urlencoded' - } - - # create the IdToken - _jwt = JWT(OP_KEYJAR, OP_BASEURL, lifetime=3600, sign=True, - sign_alg='RS256') - payload = { - 'sub': '1b2fc9341a16ae4e30082965d537', 'acr': 'PASSWORD', - 'auth_time': 1517736988, 'nonce': NONCE - } - _jws = _jwt.pack(payload=payload, recv='zls2qhN1jO6A') - - _resp = { - "state": "Oh3w3gKlvoM2ehFqlxI3HIK5", - "scope": "openid", - "access_token": "Z0FBQUFBQmFkdFF", - "token_type": "Bearer", - 'expires_in': 600, - "id_token": _jws - } - - service_context.set('issuer', OP_BASEURL) - _resp = service['accesstoken'].parse_response(json.dumps(_resp), - state=STATE) - - assert isinstance(_resp, AccessTokenResponse) - assert set(_resp['__verified_id_token'].keys()) == { - 'iss', 'nonce', 'acr', 'auth_time', 'aud', 'iat', 'exp', 'sub'} - - service['accesstoken'].update_service_context(_resp, key=STATE) - - _item = service_2['authorization'].get_item(AccessTokenResponse, - 'token_response', STATE) - - assert set(_item.keys()) == {'state', 'scope', 'access_token', - 'token_type', 'id_token', - '__verified_id_token', - 'expires_in', '__expires_at'} - - assert _item['token_type'] == 'Bearer' - assert _item['access_token'] == 'Z0FBQUFBQmFkdFF' - - # =================== User info ==================== - - info = service_2['userinfo'].get_request_parameters(state=STATE) - - assert info['url'] == '{}/userinfo'.format(OP_BASEURL) - assert info['headers'] == {'Authorization': 'Bearer Z0FBQUFBQmFkdFF'} - - op_resp = {"sub": "1b2fc9341a16ae4e30082965d537"} - - _resp = service_2['userinfo'].parse_response(json.dumps(op_resp), state=STATE) - service_2['userinfo'].update_service_context(_resp, key=STATE) - - assert isinstance(_resp, OpenIDSchema) - assert _resp.to_dict() == {'sub': '1b2fc9341a16ae4e30082965d537'} - _item = service['authorization'].get_item(OpenIDSchema, 'user_info', STATE) - assert _item.to_dict() == {'sub': '1b2fc9341a16ae4e30082965d537'} From b7a25c421ecb81cc9122be1c90a92faf467ececd Mon Sep 17 00:00:00 2001 From: roland Date: Tue, 23 Mar 2021 10:27:27 +0100 Subject: [PATCH 2/7] after isort --- src/oidcservice/service.py | 13 ++++-------- src/oidcservice/service_context.py | 3 +-- src/oidcservice/state_interface.py | 5 ++--- tests/test_01_service_context_impexp.py | 2 +- tests/test_13_oic_service.py | 28 ++++++++++--------------- tests/test_16_cc_oauth2_service.py | 2 +- tests/test_20_conversation.py | 11 +++------- 7 files changed, 23 insertions(+), 41 deletions(-) diff --git a/src/oidcservice/service.py b/src/oidcservice/service.py index abdbe6a..f42ba8e 100644 --- a/src/oidcservice/service.py +++ b/src/oidcservice/service.py @@ -1,24 +1,19 @@ """ The basic Service class upon which all the specific services are built. """ import logging -from typing import Optional -from typing import Union +from typing import Optional, Union from urllib.parse import urlparse from cryptojwt.jwt import JWT from oidcmsg.impexp import ImpExp from oidcmsg.message import Message -from oidcmsg.oauth2 import ResponseMessage -from oidcmsg.oauth2 import is_error_message +from oidcmsg.oauth2 import ResponseMessage, is_error_message from oidcservice import util from oidcservice.client_auth import factory as ca_factory from oidcservice.exception import ResponseError from oidcservice.state_interface import StateInterface -from oidcservice.util import JOSE_ENCODED -from oidcservice.util import JSON_ENCODED -from oidcservice.util import URL_ENCODED -from oidcservice.util import get_http_body -from oidcservice.util import get_http_url +from oidcservice.util import (JOSE_ENCODED, JSON_ENCODED, URL_ENCODED, + get_http_body, get_http_url) __author__ = 'Roland Hedberg' diff --git a/src/oidcservice/service_context.py b/src/oidcservice/service_context.py index c41a7bb..52af8d6 100644 --- a/src/oidcservice/service_context.py +++ b/src/oidcservice/service_context.py @@ -6,8 +6,7 @@ import hashlib import os -from cryptojwt.jwk.rsa import RSAKey -from cryptojwt.jwk.rsa import import_private_rsa_key_from_file +from cryptojwt.jwk.rsa import RSAKey, import_private_rsa_key_from_file from cryptojwt.key_bundle import KeyBundle from cryptojwt.utils import as_bytes from oidcmsg.context import OidcContext diff --git a/src/oidcservice/state_interface.py b/src/oidcservice/state_interface.py index 4360c1e..2858b8e 100644 --- a/src/oidcservice/state_interface.py +++ b/src/oidcservice/state_interface.py @@ -2,9 +2,8 @@ import json from oidcmsg.impexp import ImpExp -from oidcmsg.message import SINGLE_OPTIONAL_JSON -from oidcmsg.message import SINGLE_REQUIRED_STRING -from oidcmsg.message import Message +from oidcmsg.message import (SINGLE_OPTIONAL_JSON, SINGLE_REQUIRED_STRING, + Message) from oidcmsg.oidc import verified_claim_name from oidcservice import rndstr diff --git a/tests/test_01_service_context_impexp.py b/tests/test_01_service_context_impexp.py index 9fda094..f73caaf 100644 --- a/tests/test_01_service_context_impexp.py +++ b/tests/test_01_service_context_impexp.py @@ -1,9 +1,9 @@ import os from urllib.parse import urlsplit -from cryptojwt.key_jar import build_keyjar import pytest import responses +from cryptojwt.key_jar import build_keyjar from oidcservice.service_context import ServiceContext diff --git a/tests/test_13_oic_service.py b/tests/test_13_oic_service.py index eba8e4e..4563631 100644 --- a/tests/test_13_oic_service.py +++ b/tests/test_13_oic_service.py @@ -1,29 +1,23 @@ import json import os +import pytest from cryptojwt.exception import UnsupportedAlgorithm from cryptojwt.jws import jws from cryptojwt.jws.utils import left_hash from cryptojwt.jwt import JWT -from cryptojwt.key_jar import build_keyjar -from cryptojwt.key_jar import init_key_jar -from oidcmsg.oauth2 import AccessTokenRequest -from oidcmsg.oauth2 import AccessTokenResponse -from oidcmsg.oauth2 import AuthorizationRequest -from oidcmsg.oauth2 import AuthorizationResponse -from oidcmsg.oauth2 import Message -from oidcmsg.oidc import IdToken -from oidcmsg.oidc import OpenIDSchema -from oidcmsg.oidc import RegistrationRequest -from oidcmsg.oidc import verified_claim_name -from oidcmsg.oidc.session import CheckIDRequest -from oidcmsg.oidc.session import CheckSessionRequest -from oidcmsg.oidc.session import EndSessionRequest -import pytest +from cryptojwt.key_jar import build_keyjar, init_key_jar +from oidcmsg.oauth2 import (AccessTokenRequest, AccessTokenResponse, + AuthorizationRequest, AuthorizationResponse, + Message) +from oidcmsg.oidc import (IdToken, OpenIDSchema, RegistrationRequest, + verified_claim_name) +from oidcmsg.oidc.session import (CheckIDRequest, CheckSessionRequest, + EndSessionRequest) from oidcservice.exception import ParameterError -from oidcservice.oidc.registration import add_jwks_uri_or_jwks -from oidcservice.oidc.registration import response_types_to_grant_types +from oidcservice.oidc.registration import (add_jwks_uri_or_jwks, + response_types_to_grant_types) from oidcservice.service_context import ServiceContext from oidcservice.service_factory import service_factory diff --git a/tests/test_16_cc_oauth2_service.py b/tests/test_16_cc_oauth2_service.py index 6ca05d7..14a1385 100644 --- a/tests/test_16_cc_oauth2_service.py +++ b/tests/test_16_cc_oauth2_service.py @@ -1,5 +1,5 @@ -from oidcmsg.oauth2 import AccessTokenResponse import pytest +from oidcmsg.oauth2 import AccessTokenResponse from oidcservice import rndstr from oidcservice.service_context import ServiceContext diff --git a/tests/test_20_conversation.py b/tests/test_20_conversation.py index e5e8302..a39b2a8 100644 --- a/tests/test_20_conversation.py +++ b/tests/test_20_conversation.py @@ -1,17 +1,12 @@ #!/usr/bin/env python3 import json import time -from urllib.parse import parse_qs -from urllib.parse import urlparse +from urllib.parse import parse_qs, urlparse from cryptojwt.jwt import JWT from cryptojwt.key_jar import KeyJar -from oidcmsg.oidc import AccessTokenResponse -from oidcmsg.oidc import AuthorizationResponse -from oidcmsg.oidc import JRD -from oidcmsg.oidc import Link -from oidcmsg.oidc import OpenIDSchema -from oidcmsg.oidc import ProviderConfigurationResponse +from oidcmsg.oidc import (JRD, AccessTokenResponse, AuthorizationResponse, + Link, OpenIDSchema, ProviderConfigurationResponse) from oidcservice.oidc import DEFAULT_SERVICES from oidcservice.oidc.webfinger import WebFinger From 1c405c0003f3b1d9bc3075a7bc74f8477a8c964e Mon Sep 17 00:00:00 2001 From: roland Date: Tue, 23 Mar 2021 11:03:51 +0100 Subject: [PATCH 3/7] upgrade pip ?! --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index eb54c0d..f0db04c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ addons: packages: - install: +- pip install --upgrade pip - pip install codecov - pip install tox - pip install isort From f3af8bd494630e45cad11a833a73778949822889 Mon Sep 17 00:00:00 2001 From: roland Date: Tue, 23 Mar 2021 11:16:06 +0100 Subject: [PATCH 4/7] Install rustc and cargo from packages --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f0db04c..f223264 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,8 @@ python: addons: apt: packages: - - + - rustc + - cargo install: - pip install --upgrade pip - pip install codecov From d5a0a2b3d9e0bbb25caaca562a5c35c49ed80674 Mon Sep 17 00:00:00 2001 From: roland Date: Tue, 23 Mar 2021 14:44:16 +0100 Subject: [PATCH 5/7] Don't run Travis with python3.6. Will fail for reason that has nothing to do with this package. --- .travis.yml | 2 +- tox.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f223264..3fc07b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ sudo: false language: python python: -- 3.6 - 3.7 - 3.8 +- 3.9 - pypy3 addons: apt: diff --git a/tox.ini b/tox.ini index efcafe0..28043f5 100755 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{36,37,38},docs,quality +envlist = py{37,38,39},docs,quality [testenv] passenv = CI TRAVIS TRAVIS_* From d1dcc683cc95dc68537b885b57281667b5193c4d Mon Sep 17 00:00:00 2001 From: roland Date: Wed, 24 Mar 2021 09:22:27 +0100 Subject: [PATCH 6/7] Explicit on distro to use. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 3fc07b5..5d6015d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,5 @@ sudo: false +dist: bionic language: python python: - 3.7 From a4d82f50c3f4a3f7ac6ec946bd8296c1c9f06ff2 Mon Sep 17 00:00:00 2001 From: roland Date: Wed, 31 Mar 2021 11:20:39 +0200 Subject: [PATCH 7/7] Make it work with the new persistent handling. --- .coverage | 2 +- setup.py | 2 +- src/oidcservice/client_auth.py | 45 ++-- src/oidcservice/oauth2/authorization.py | 11 +- .../oauth2/provider_info_discovery.py | 10 +- src/oidcservice/oauth2/utils.py | 6 +- src/oidcservice/oidc/access_token.py | 13 +- .../oidc/add_on/pushed_authorization.py | 2 +- src/oidcservice/oidc/add_on/status_check.py | 2 +- src/oidcservice/oidc/authorization.py | 29 +-- .../oidc/provider_info_discovery.py | 12 +- src/oidcservice/oidc/read_registration.py | 4 +- src/oidcservice/oidc/refresh_access_token.py | 3 +- src/oidcservice/oidc/registration.py | 23 +- src/oidcservice/oidc/userinfo.py | 9 +- src/oidcservice/oidc/utils.py | 5 +- src/oidcservice/oidc/webfinger.py | 2 +- src/oidcservice/service.py | 49 ++-- src/oidcservice/service_context.py | 52 ++-- tests/request123456.jwt | 2 +- tests/test_01_service_context.py | 22 +- tests/test_01_service_context_impexp.py | 38 ++- tests/test_07_service_impexp.py | 62 +++-- tests/test_09_client_auth.py | 222 ++++++++++++++---- tests/test_10_oauth2_service.py | 43 +++- tests/test_13_oic_service.py | 52 ++-- tests/test_15_oic_utils.py | 4 +- tests/test_20_conversation.py | 14 +- tests/test_21_pushed_auth.py | 6 +- 29 files changed, 471 insertions(+), 275 deletions(-) diff --git a/.coverage b/.coverage index 416b191..8a542a3 100644 --- a/.coverage +++ b/.coverage @@ -1 +1 @@ -!coverage.py: This is a private format, don't read it directly!{"lines":{"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/__init__.py":[1,2,5,6,7,8,10,11,13,15,16,17,21,22,23,24,25,27,29,30,32,35,45,48,60,42,57],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/service_context.py":[4,5,6,7,9,10,11,12,13,14,16,20,21,22,25,26,27,30,31,32,38,39,40,43,44,45,48,49,50,53,58,59,60,61,62,63,64,80,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,111,179,182,204,227,252,269,290,293,112,113,114,116,117,119,121,123,124,125,126,127,128,129,130,131,132,133,134,135,137,139,141,142,294,143,144,146,147,149,150,151,152,154,157,158,159,160,161,163,164,165,166,167,169,174,175,176,177,171,195,198,199,200,259,260,291,261,262,263,264,265,267,276,277,278,279,286,288,212,213,214,217,219,222,224,225,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,155,202,215,216,220],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/state_interface.py":[1,2,4,5,6,7,8,10,13,14,16,17,18,19,20,21,22,27,28,29,30,34,35,36,39,43,50,57,61,68,76,77,80,83,87,100,122,134,152,194,235,257,272,282,293,304,315,326,337,348,359,377,84,85,37,41,171,172,145,94,95,96,173,174,191,366,367,373,374,375,110,111,98,115,116,120,117,118,210,212,213,214,218,219,225,226,227,228,229,230,231,215,216,233,112,113,146,147,176,177,183,184,185,186,187,188,189,148,369,280,244,245,246,250,251,255,291,266,267,268,346,253,254,220,221,302,149],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/util.py":[1,2,3,4,6,7,9,11,13,14,15,18,44,67,74,89,28,29,30,31,32,35,36,37,54,55,57,58,33,91,76,82,83,86,92,93,41,39],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/service.py":[1,2,3,4,5,7,8,9,10,11,13,14,15,16,17,18,19,20,21,23,25,27,29,31,34,35,36,37,38,39,40,41,42,43,44,45,46,49,50,51,52,53,54,55,56,60,84,121,137,157,172,182,219,241,257,270,271,293,305,306,334,404,422,433,456,465,487,559,575,591,61,62,63,67,68,69,77,80,81,82,192,198,199,147,129,130,131,132,135,148,149,155,203,209,93,101,102,105,106,107,109,110,111,112,113,114,116,119,214,216,165,167,170,103,355,356,357,358,300,359,360,362,252,255,364,365,367,368,369,373,374,314,315,317,318,319,320,281,283,291,322,331,377,378,379,380,263,264,382,385,397,400,413,414,416,417,420,603,604,605,606,607,608,610,611,612,615,616,620,622,625,626,630,193,150,151,205,206,117,507,510,512,523,524,526,530,532,466,467,468,485,534,537,540,541,542,545,551,553,557,431,168,284,285,231,232,234,235,236,237,286,287,387,388,394,395,398,253,70,71,74,75,288,289,508,546,547,548,549,527,457,458,459,460,461,462,463,528,108,65,441,442,443,446,447,448,450,454,389,392,618,569,572,451,180,323,324,325,326,327,328,329],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/client_auth.py":[1,2,3,4,6,7,8,9,10,11,12,14,16,18,21,22,25,26,30,50,54,56,60,69,80,82,93,101,118,148,164,189,197,199,221,235,272,273,275,313,314,316,335,351,369,373,375,393,405,426,445,477,496,530,546,553,555,558,562,565,567,570,576,577,578,579,580,581,584,587,603,174,175,177,178,180,112,84,85,91,113,95,96,99,115,116,182,184,156,157,158,159,162,127,136,137,138,139,142,143,144,145,146,186,86,87,88,97,98,128,129,130,131,132,133,288,291,250,251,252,256,258,259,293,298,301,302,303,310,305,306,307,308,253,254,261,262,263,266,267,268,269,346,324,325,326,327,328,330,333,348,231,208,209,219,232,210,211,541,504,511,515,478,480,446,450,451,452,453,469,473,475,482,485,427,428,429,431,438,571,443,487,488,489,490,494,41,43,44,45,46,47,516,518,519,520,521,524,525,526,527,528,543,505,506,510,559,471,474,556,384,385,389,391,596,597,598,600,599,608,609,289,89,90],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/exception.py":[2,6,7,12,13,16,17,20,21,24,25,28,29,32,33,36,37,40,41,44,45,48,49,52,53,56,57,60,64,66,71,72,75,76,79,80,83,84,87,88,91,92,95,96,99,100,103,104,107,108,111,112,115,116,8,9],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/__init__.py":[5,9,12,15,18,21,25,26,29,30,31,35,36,37,38,39],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/webfinger.py":[1,2,4,5,6,7,9,10,12,14,16,17,18,19,20,23,26,27,28,29,30,31,32,33,36,42,65,75,147,37,38,40,83,86,87,91,92,93,94,95,100,103,104,143,144,145,101,96,97,98,116,117,124,127,140,141,67,68,69,72,70,73,128,129,130,132,133,137,138,105,106,107,108,110,111,112,120,121,122,123,149,152,153,163,154,155,156,157,158,159,160,161,43,44,48,49,50,51,52,53,54,55,57,61,62,63],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/service_factory.py":[1,2,3,4,6,9,33,10,11,12,14,15,16,17,18,20,21,22,24,25,26,27,28,19],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/registration.py":[1,3,4,6,7,9,11,15,16,17,18,19,20,24,41,53,71,96,97,98,99,100,101,102,103,104,106,115,130,143,107,108,109,110,111,112,113,25,27,28,29,30,31,36,38,116,117,120,121,122,123,125,126,127,128,42,43,44,45,50,63,64,65,68,72,76,79,80,81,85,86,87,88,93,131,132,133,138,141,66,46,47,48,90,91,82,83,73,75,144,147,148,149,150,151,152,153,154,156,157,158,159,160,161,162,163,167,168,169],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/provider_info_discovery.py":[1,3,4,6,7,9,11,15,17,19,20,22,24,25,27,29,30,31,32,34,35,36,37,40,43,44,48,72,73,74,75,77,82,91,78,79,80,83,84,105,108,110,112,113,114,115,116,118,119,131,132,133,156,135,136,144,145,146,147,148,149,137,139,140,159,160,163,164,165,167,171,172,169,170,166,161,174,175,85,120,121,124,125,126,127,128,129,57,58,61,62,67,69],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/__init__.py":[5,8,11,14],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/provider_info_discovery.py":[1,2,4,5,6,8,9,10,12,15,16,17,18,19,20,21,22,24,28,44,54,78,88,105,143,25,26,52,34,35,39,42,116,117,118,55,56,62,65,69,70,71,72,76,122,123,125,92,93,94,95,129,130,136,137,141,97,98,102,103,80,84,85,86],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/__init__.py":[1,4,5,6,7],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/pkce.py":[1,3,4,6,7,8,10,13,62,80,85,95,96,98,99,102,104,105,106,24,26,27,32,33,35,36,40,42,44,46,51,52,54,56,57,59,72,73,74,75,76,77,81,82],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/utils.py":[1,4,17,49,6,7,14,19,21,24,25,44,46,51,52,8,9,10,26,27,32,33,34,35,37,39,40,42,38],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/utils.py":[1,3,4,5,7,10,68,19,20,21,22,23,24,25,26,28,31,32,33,34,35,36,41,45,46,48,49,50,51,53,56,62,63,65,78,79,81,82,83,84,87,88],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/authorization.py":[1,3,4,5,7,8,9,10,11,12,15,17,20,21,22,23,25,32,45,65,107,124,142,179,208,26,27,28,29,30,214,216,217,218,221,222,223,225,226,233,234,235,236,238,239,242,46,47,48,49,61,63,33,34,35,36,37,41,42,43,66,69,70,76,77,83,84,85,87,90,91,92,93,94,98,105,187,188,189,190,192,196,197,198,199,205,206,78,79,99,102,103,201,203,145,108,109,110,111,112,113,117,118,119,120,121,122,146,148,149,151,154,155,157,158,159,160,161,163,165,168,171,172,174,175,100,177,131,132,133,137,138,139,140,53,54,59,55,56,57,240,38,39,71,72,73,227,228,229,230,231],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/authorization.py":[1,2,4,5,6,7,9,11,13,16,17,18,19,20,21,22,23,24,26,32,37,43,54,27,28,29,30,64,65,66,70,77,44,46,52,39,40,41],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/access_token.py":[1,2,4,5,6,8,9,10,12,14,17,18,19,20,25,29,64,85,26,27,35,39,40,41,44,45,52,53,54,55,57,58,62,65,66,67,68,69,79,80,81,83,86,87,88,89,90,71,72,73,74,46,47,48,49,50,77],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/access_token.py":[1,2,4,5,6,8,9,11,14,15,16,17,18,19,20,21,22,23,24,25,27,32,37,28,29,30,44,45,47,48,50,51,53,54,56,59,60,62],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/refresh_access_token.py":[1,3,6,7,8,9,11],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/refresh_access_token.py":[1,2,4,5,6,8,9,11,14,15,16,17,18,19,20,21,22,23,25,30,35,26,27,28,37,38,40,41,42,44,45,46,48,51,52,54],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/userinfo.py":[1,3,4,5,7,8,10,13,14,15,19,30,31,32,33,34,35,36,37,38,40,45,59,107,41,42,43,46,49,52,53,54,57,27,113,115,116,117,120,121,128,129,130,131,133,60,61,62,63,66,67,71,74,75,76,77,104,105,79,80,81,82,83,84,90,91,92,94,95,85,86,87,88,122,123,124,125,126],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/pushed_authorization.py":[1,3,4,5,7,9,12,54,65,66,68,70,71,72,73,76,19,22,25,26,27,29,30,31,32,34,37,38,39,42,43,44,45,46,47,48,50],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/dpop.py":[1,2,4,5,6,7,9,11,14,17,18,19,21,22,23,24,26,27,29,39,48,55,62,88,89,146,154,157,159,102,103,105,106,107,110,111,112,113,114,116,120,121,122,125,126,127,128,129,130,131,134,30,31,40,42,43,44,46,33,34,135,136,56,57,58,59,60,138,141,143],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/client_credentials/cc_refresh_access_token.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/client_credentials/cc_access_token.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/client_credentials/__init__.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/end_session.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/check_id.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/read_registration.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/check_session.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/status_check.py":[]}} \ No newline at end of file +!coverage.py: This is a private format, don't read it directly!{"lines":{"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/__init__.py":[1,2,5,6,7,8,10,11,13,15,16,17,21,22,23,24,25,27,29,30,32,35,45,48,60,42,61,57],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/service_context.py":[4,5,6,7,9,10,11,12,13,15,19,20,21,24,25,26,29,30,31,37,38,39,42,43,44,47,48,49,52,57,58,59,60,61,62,63,79,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,113,184,187,209,232,257,274,295,298,114,115,116,118,119,121,123,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,142,144,146,147,299,148,149,151,152,154,155,156,157,159,162,163,164,165,166,168,169,170,171,172,174,179,180,181,182,176,200,203,204,205,264,265,266,267,268,269,270,272,281,282,283,284,291,293,217,218,219,222,224,227,229,230,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,160,296,207,220,221,225],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/state_interface.py":[1,2,4,5,7,9,12,13,15,16,17,18,19,20,21,26,27,28,29,33,34,35,38,42,49,56,60,67,75,76,79,82,86,99,121,133,151,193,234,256,271,281,292,303,314,325,336,347,358,376,83,84,36,40,170,171,144,93,94,95,172,173,190,365,366,372,373,374,109,110,97,114,115,119,116,117,209,211,212,213,217,218,224,225,226,227,228,229,230,214,215,232,111,112,145,146,175,176,182,183,184,185,186,187,188,147,368,279,243,244,245,249,250,254,290,265,266,267,345,252,253,219,220,301,148],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/util.py":[1,2,3,4,6,7,9,11,13,14,15,18,44,67,74,89,28,29,30,31,32,35,36,37,54,55,57,58,33,91,76,82,83,86,92,93,41,39],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/service.py":[1,2,3,4,5,7,8,9,10,11,12,13,15,16,17,18,19,20,21,22,24,26,28,30,32,35,36,37,38,39,40,41,42,43,44,45,46,47,50,51,52,53,54,55,56,59,62,86,123,139,159,174,184,221,243,259,272,273,295,307,308,336,406,424,435,458,467,488,560,576,592,63,64,65,69,70,71,79,82,83,84,194,200,201,149,131,132,133,134,137,150,151,157,205,211,95,103,104,107,108,109,111,112,113,114,115,116,118,121,216,218,167,169,172,105,357,358,359,360,302,361,362,364,254,257,366,367,369,370,371,375,376,316,317,319,320,321,322,283,285,293,324,333,379,380,381,382,265,266,384,387,399,402,415,416,418,419,422,604,605,606,607,608,609,611,612,613,616,617,618,623,625,628,630,195,152,153,207,208,119,508,511,513,524,525,527,531,533,468,469,470,486,535,538,541,542,543,546,552,554,558,433,170,443,444,445,448,449,450,452,456,286,287,233,234,236,237,238,239,288,289,389,390,396,397,400,255,72,73,76,77,290,291,509,547,548,549,550,528,459,460,461,462,463,464,465,529,110,67,391,394,620,621,570,573,453,182,325,326,327,328,329,330,331],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/client_auth.py":[1,2,3,4,6,7,8,9,10,11,12,13,15,17,19,22,23,26,27,31,51,55,57,61,70,81,83,94,102,119,149,165,190,198,200,222,236,273,274,276,314,315,317,336,352,370,374,376,394,406,428,447,479,501,535,551,558,560,563,567,570,572,575,581,582,583,584,585,586,589,592,608,175,176,178,179,181,113,85,86,92,114,96,97,100,116,117,183,185,157,158,159,160,163,128,137,138,139,140,143,144,145,146,147,187,87,88,89,98,99,129,130,131,132,133,134,289,292,251,252,253,257,259,260,294,299,302,303,304,311,306,307,308,309,254,255,262,263,264,267,268,269,270,347,325,326,327,328,329,331,334,349,232,209,210,220,233,211,212,213,214,215,217,546,509,516,520,480,482,448,452,453,454,455,471,475,477,484,487,429,430,431,433,440,576,445,489,492,493,494,495,499,42,44,45,46,47,48,521,523,524,525,526,529,530,531,532,533,548,510,511,515,564,485,432,418,419,420,421,424,426,441,442,443,457,458,459,460,461,462,463,465,466,467,468,469,473,476,561,385,386,390,392,601,602,603,605,604,362,363,364,365,367,366,613,614,290,90,91],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/exception.py":[2,6,7,12,13,16,17,20,21,24,25,28,29,32,33,36,37,40,41,44,45,48,49,52,53,56,57,60,64,66,71,72,75,76,79,80,83,84,87,88,91,92,95,96,99,100,103,104,107,108,111,112,115,116,8,9],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/__init__.py":[5,9,12,15,18,21,25,26,29,30,31,35,36,37,38,39],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/webfinger.py":[1,2,4,5,6,7,9,10,12,14,16,17,18,19,20,23,26,27,28,29,30,31,32,33,36,42,65,75,147,37,38,40,83,86,87,91,92,93,94,95,100,103,104,143,144,145,101,96,97,98,116,117,124,127,140,141,67,68,69,72,70,73,128,129,130,132,133,137,138,105,106,107,108,110,111,112,120,121,122,123,149,152,153,163,154,155,156,157,158,159,160,161,43,44,48,49,50,51,52,53,54,55,57,61,62,63],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/service_factory.py":[1,2,3,4,6,9,33,10,11,12,14,15,16,17,18,20,21,22,24,25,26,27,28,19],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/registration.py":[1,3,4,6,7,9,11,14,15,16,17,18,19,23,40,52,70,95,96,97,98,99,100,101,102,103,105,114,129,142,106,107,108,109,110,111,112,24,26,27,28,29,30,35,37,115,116,119,120,121,122,124,125,126,127,41,42,43,44,49,62,63,64,67,71,75,78,79,80,84,85,86,87,92,130,131,132,137,140,65,45,46,47,89,90,81,82,72,74,143,146,147,148,149,150,151,152,153,155,156,157,158,159,160,161,165,166],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/provider_info_discovery.py":[1,3,4,6,7,9,11,15,17,19,20,22,24,25,27,29,30,31,32,34,35,36,37,40,43,44,48,72,73,74,75,77,82,91,78,79,80,83,84,105,108,110,112,113,114,115,116,118,119,131,132,133,156,135,136,144,145,146,147,148,149,137,139,140,159,160,163,164,165,167,171,172,169,170,166,161,174,175,85,120,121,124,125,126,127,128,129,57,58,61,62,67,69],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/__init__.py":[5,8,11,14],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/provider_info_discovery.py":[1,2,4,5,6,8,9,10,12,15,16,17,18,19,20,21,22,24,28,44,54,78,88,105,143,25,26,52,34,35,39,42,116,117,118,55,56,62,65,69,70,71,72,76,122,123,125,92,93,94,95,129,130,136,137,141,97,98,102,103,80,84,85,86],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/__init__.py":[1,4,5,6,7],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/pkce.py":[1,3,4,6,7,8,10,13,62,80,85,95,96,98,99,102,104,105,106,24,26,27,32,33,35,36,40,42,44,46,51,52,54,56,57,59,72,73,74,75,76,77,81,82],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/utils.py":[1,4,17,49,6,7,14,19,21,24,25,44,46,51,52,8,9,10,26,27,32,33,34,35,37,39,40,42,38],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/utils.py":[1,3,4,5,7,10,67,19,20,21,22,23,24,25,26,28,31,32,33,34,35,40,44,45,47,48,49,50,52,55,61,62,64,77,78,80,81,82,83,86,87],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/authorization.py":[1,3,4,5,7,8,9,10,11,12,15,17,20,21,22,23,25,32,45,65,107,124,142,180,209,26,27,28,29,30,215,217,218,219,222,223,224,226,227,234,235,236,237,239,240,243,46,47,48,49,61,63,33,34,35,36,37,41,42,43,66,69,70,76,77,83,84,85,87,90,91,92,93,94,98,105,188,189,190,191,193,197,198,199,200,206,207,78,79,99,102,103,202,204,145,108,109,110,111,112,113,117,118,119,120,121,122,146,148,149,151,154,155,156,158,159,160,161,162,164,166,169,172,173,175,176,100,178,131,132,133,137,138,139,140,53,54,59,55,56,57,241,38,39,71,72,73,228,229,230,231,232],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/authorization.py":[1,2,4,5,6,7,9,10,11,12,14,17,18,19,20,21,22,23,24,25,27,33,38,44,55,28,29,30,31,65,66,67,71,78,45,47,53,40,41,42,72,73,74,75,76,77,34,36],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/access_token.py":[1,2,4,5,6,8,9,10,12,14,17,18,19,20,25,29,66,87,26,27,35,39,40,41,42,43,46,47,54,55,56,57,59,60,64,67,68,69,70,71,81,82,83,85,88,89,90,91,73,74,75,76,48,49,50,51,52,79],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/access_token.py":[1,2,4,5,6,8,9,11,14,15,16,17,18,19,20,21,22,23,24,25,27,32,37,28,29,30,44,45,47,48,50,51,53,54,56,59,60,62],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/refresh_access_token.py":[1,3,6,7,8,9,11],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/refresh_access_token.py":[1,2,4,5,6,8,9,11,14,15,16,17,18,19,20,21,22,23,25,30,35,26,27,28,37,38,40,41,42,44,45,46,48,51,52,54],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/userinfo.py":[1,3,4,5,7,8,10,13,14,15,19,30,31,32,33,34,35,36,37,38,40,45,59,109,41,42,43,46,49,52,53,54,57,27,115,117,118,119,120,123,124,131,132,133,134,136,60,61,62,63,66,67,71,74,75,76,77,106,107,79,80,81,82,83,84,90,91,92,94,95,85,86,87,88,125,126,127,128,129],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/pushed_authorization.py":[1,3,4,5,7,9,12,54,65,66,68,70,71,72,73,76,19,22,25,26,27,29,30,31,32,34,37,38,39,42,43,44,45,46,47,48,50],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/dpop.py":[1,2,4,5,6,7,9,11,14,17,18,19,21,22,23,24,26,27,29,39,48,55,62,88,89,146,154,157,159,102,103,105,106,107,110,111,112,113,114,116,120,121,122,125,126,127,128,129,130,131,134,30,31,40,42,43,44,46,33,34,135,136,56,57,58,59,60,138,141,143],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/client_credentials/cc_refresh_access_token.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/client_credentials/cc_access_token.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oauth2/client_credentials/__init__.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/end_session.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/check_id.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/read_registration.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/check_session.py":[],"/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/oidcservice-1.2.0-py3.7.egg/oidcservice/oidc/add_on/status_check.py":[]}} \ No newline at end of file diff --git a/setup.py b/setup.py index b72d163..b2a022b 100755 --- a/setup.py +++ b/setup.py @@ -69,7 +69,7 @@ "Topic :: Software Development :: Libraries :: Python Modules"], install_requires=[ "pyyaml>=5.1.0", - 'oidcmsg>=1.2.0', + 'oidcmsg>=1.2.1', 'requests' ], tests_require=[ diff --git a/src/oidcservice/client_auth.py b/src/oidcservice/client_auth.py index b7f880f..b00a8c6 100755 --- a/src/oidcservice/client_auth.py +++ b/src/oidcservice/client_auth.py @@ -4,6 +4,7 @@ from urllib.parse import quote_plus from cryptojwt.exception import MissingKey +from cryptojwt.exception import UnsupportedAlgorithm from cryptojwt.jws.jws import SIGNER_ALGS from cryptojwt.jws.utils import alg2keytype from oidcmsg.message import VREQUIRED @@ -87,7 +88,7 @@ def _get_passwd(request, service, **kwargs): try: passwd = request["client_secret"] except KeyError: - passwd = service.service_context.get('client_secret') + passwd = service.service_context.client_secret return passwd @staticmethod @@ -95,7 +96,7 @@ def _get_user(service, **kwargs): try: user = kwargs["user"] except KeyError: - user = service.service_context.get('client_id') + user = service.service_context.client_id return user def _get_authentication_token(self, request, service, **kwargs): @@ -128,7 +129,7 @@ def _with_or_without_client_id(request, service): 'grant_type'] == 'authorization_code': if 'client_id' not in request: try: - request['client_id'] = service.service_context.get('client_id') + request['client_id'] = service.service_context.client_id except AttributeError: pass else: @@ -210,13 +211,13 @@ def modify_request(self, request, service, **kwargs): try: request["client_secret"] = kwargs["client_secret"] except (KeyError, TypeError): - if _context.get('client_secret'): - request["client_secret"] = _context.get('client_secret') + if _context.client_secret: + request["client_secret"] = _context.client_secret else: raise AuthnFailure("Missing client secret") - # Add the client_id to the request - request["client_id"] = _context.get('client_id') + # Set the client_id in the the request + request["client_id"] = _context.client_id def construct(self, request, service=None, http_args=None, **kwargs): """ @@ -413,13 +414,14 @@ def _get_key_by_kid(kid, algorithm, service_context): :py:class:`oidcservice.service_context.ServiceContext` instance :return: A matching key """ - _key = service_context.keyjar.get_key_by_kid(kid) - if _key: - ktype = alg2keytype(algorithm) - if _key.kty != ktype: - raise MissingKey("Wrong key type") + # signing so using my keys + for _key in service_context.keyjar.get_issuer_keys(""): + if kid == _key.kid: + ktype = alg2keytype(algorithm) + if _key.kty != ktype: + raise MissingKey("Wrong key type") - return _key + return _key raise MissingKey("No key with kid:%s" % kid) @@ -448,13 +450,13 @@ def _get_audience_and_algorithm(self, context, **kwargs): # audience for the signed JWT depends on which endpoint # we're talking to. if 'authn_endpoint' in kwargs and kwargs['authn_endpoint'] in ['token_endpoint']: - reg_resp = context.get("registration_response") + reg_resp = context.registration_response if reg_resp: - algorithm = reg_resp.get("token_endpoint_auth_signing_alg") + algorithm = reg_resp["token_endpoint_auth_signing_alg"] else: algorithm = context.client_preferences.get("token_endpoint_auth_signing_alg") if algorithm is None: - _pi = context.get("provider_info") + _pi = context.provider_info try: algs = _pi["token_endpoint_auth_signing_alg_values_supported"] except KeyError: @@ -466,9 +468,9 @@ def _get_audience_and_algorithm(self, context, **kwargs): algorithm = alg break - audience = context.get('provider_info')['token_endpoint'] + audience = context.provider_info['token_endpoint'] else: - audience = context.get('provider_info')['issuer'] + audience = context.provider_info['issuer'] if not algorithm: algorithm = self.choose_algorithm(**kwargs) @@ -484,6 +486,9 @@ def _construct_client_assertion(self, service, **kwargs): else: signing_key = self._get_signing_key(algorithm, _context) + if not signing_key: + raise UnsupportedAlgorithm(algorithm) + try: _args = {'lifetime': kwargs['lifetime']} except KeyError: @@ -491,7 +496,7 @@ def _construct_client_assertion(self, service, **kwargs): # construct the signed JWT with the assertions and add # it as value to the 'client_assertion' claim of the request - return assertion_jwt(_context.get('client_id'), signing_key, audience, algorithm, **_args) + return assertion_jwt(_context.client_id, signing_key, audience, algorithm, **_args) def modify_request(self, request, service, **kwargs): """ @@ -593,7 +598,7 @@ def valid_service_context(service_context, when=0): :param when: A time stamp against which the expiration time is to be checked :return: True if the client_secret is still valid """ - eta = service_context.get('client_secret_expires_at', 0) + eta = service_context.client_secret_expires_at now = when or utc_time_sans_frac() if eta != 0 and eta < now: return False diff --git a/src/oidcservice/oauth2/authorization.py b/src/oidcservice/oauth2/authorization.py index 9dfb94c..b488902 100644 --- a/src/oidcservice/oauth2/authorization.py +++ b/src/oidcservice/oauth2/authorization.py @@ -6,8 +6,9 @@ from oidcmsg.oauth2 import ResponseMessage from oidcmsg.time_util import time_sans_frac -from oidcservice.oauth2.utils import (get_state_parameter, pick_redirect_uris, - set_state_parameter) +from oidcservice.oauth2.utils import get_state_parameter +from oidcservice.oauth2.utils import pick_redirect_uris +from oidcservice.oauth2.utils import set_state_parameter from oidcservice.service import Service LOGGER = logging.getLogger(__name__) @@ -45,7 +46,7 @@ def gather_request_args(self, **kwargs): if 'redirect_uri' not in ar_args: try: - ar_args['redirect_uri'] = self.service_context.get('redirect_uris')[0] + ar_args['redirect_uri'] = self.service_context.redirect_uris[0] except (KeyError, AttributeError): raise MissingParameter('redirect_uri') @@ -68,8 +69,8 @@ def post_parse_response(self, response, **kwargs): pass else: if _key: - item = self.get_item(oauth2.AuthorizationRequest, - 'auth_request', _key) + item = self.service_context.state.get_item(oauth2.AuthorizationRequest, + 'auth_request', _key) try: response["scope"] = item["scope"] except KeyError: diff --git a/src/oidcservice/oauth2/provider_info_discovery.py b/src/oidcservice/oauth2/provider_info_discovery.py index ccdec11..509b4e5 100644 --- a/src/oidcservice/oauth2/provider_info_discovery.py +++ b/src/oidcservice/oauth2/provider_info_discovery.py @@ -32,7 +32,7 @@ def get_endpoint(self): :return: Service endpoint """ try: - _iss = self.service_context.get('issuer') + _iss = self.service_context.issuer except AttributeError: _iss = self.endpoint @@ -115,12 +115,12 @@ def _update_service_context(self, resp): # url that was used as service endpoint (without the .well-known part) if "issuer" in resp: _pcr_issuer = self._verify_issuer(resp, - self.service_context.get('issuer')) + self.service_context.issuer) else: # No prior knowledge - _pcr_issuer = self.service_context.get('issuer') + _pcr_issuer = self.service_context.issuer - self.service_context.set('issuer', _pcr_issuer) - self.service_context.set('provider_info', resp) + self.service_context.issuer= _pcr_issuer + self.service_context.provider_info= resp self._set_endpoints(resp) diff --git a/src/oidcservice/oauth2/utils.py b/src/oidcservice/oauth2/utils.py index ea297e0..78e45ab 100644 --- a/src/oidcservice/oauth2/utils.py +++ b/src/oidcservice/oauth2/utils.py @@ -21,12 +21,12 @@ def pick_redirect_uris(request_args=None, service=None, **kwargs): if 'redirect_uri' in request_args: return request_args, {} - _callback = _context.get('callback') + _callback = _context.callback if _callback: try: _response_type = request_args['response_type'] except KeyError: - _response_type = _context.get('behaviour')['response_types'][0] + _response_type = _context.behaviour['response_types'][0] request_args['response_type'] = _response_type try: @@ -41,7 +41,7 @@ def pick_redirect_uris(request_args=None, service=None, **kwargs): else: request_args['redirect_uri'] = _callback['implicit'] else: - request_args['redirect_uri'] = _context.get('redirect_uris')[0] + request_args['redirect_uri'] = _context.redirect_uris[0] return request_args, {} diff --git a/src/oidcservice/oidc/access_token.py b/src/oidcservice/oidc/access_token.py index fe60705..9444f92 100644 --- a/src/oidcservice/oidc/access_token.py +++ b/src/oidcservice/oidc/access_token.py @@ -36,12 +36,14 @@ def gather_verify_arguments(self): # Default is RS256 kwargs = { - 'client_id': _ctx.get('client_id'), 'iss': _ctx.get('issuer'), - 'keyjar': _ctx.keyjar, 'verify': True, + 'client_id': _ctx.client_id, + 'iss': _ctx.issuer, + 'keyjar': _ctx.keyjar, + 'verify': True, 'skew': _ctx.clock_skew, } - _reg_resp = _ctx.get('registration_response') + _reg_resp = _ctx.registration_response if _reg_resp: for attr, param in IDT2REG.items(): try: @@ -54,7 +56,7 @@ def gather_verify_arguments(self): except KeyError: pass - _verify_args = _ctx.get('behaviour').get("verify_args") + _verify_args = _ctx.behaviour.get("verify_args") if _verify_args: if _verify_args: kwargs.update(_verify_args) @@ -84,7 +86,6 @@ def update_service_context(self, resp, key='', **kwargs): def get_authn_method(self): try: - return self.service_context.get('behaviour')[ - 'token_endpoint_auth_method'] + return self.service_context.behaviour['token_endpoint_auth_method'] except KeyError: return self.default_authn_method diff --git a/src/oidcservice/oidc/add_on/pushed_authorization.py b/src/oidcservice/oidc/add_on/pushed_authorization.py index 779de1c..743bd80 100644 --- a/src/oidcservice/oidc/add_on/pushed_authorization.py +++ b/src/oidcservice/oidc/add_on/pushed_authorization.py @@ -35,7 +35,7 @@ def push_authorization(request_args, service, **kwargs): # Send it to the Pushed Authorization Request Endpoint resp = method_args["http_client"].get( - service.service_context.get('provider_info')["pushed_authorization_request_endpoint"], + service.service_context.provider_info["pushed_authorization_request_endpoint"], data=_body ) diff --git a/src/oidcservice/oidc/add_on/status_check.py b/src/oidcservice/oidc/add_on/status_check.py index 0f6d5aa..7f28683 100644 --- a/src/oidcservice/oidc/add_on/status_check.py +++ b/src/oidcservice/oidc/add_on/status_check.py @@ -9,7 +9,7 @@ def get_session_status_page(service_context, looked_for_state): :param looked_for_state: Expecting state to be ? (changed/unchanged) """ _msg = open(service_context.add_on['status_check']['template_file']).read() - _csi = service_context.get('provider_info')['check_session_iframe'] + _csi = service_context.provider_info['check_session_iframe'] _mod_msg = _msg.replace("{check_session_iframe}", _csi) if looked_for_state == "changed": _mod_msg = _mod_msg.replace( diff --git a/src/oidcservice/oidc/authorization.py b/src/oidcservice/oidc/authorization.py index 876afe8..9ad803b 100644 --- a/src/oidcservice/oidc/authorization.py +++ b/src/oidcservice/oidc/authorization.py @@ -39,7 +39,7 @@ def set_state(self, request_args, **kwargs): _state = '' request_args['state'] = self.service_context.state.create_state( - self.service_context.get('issuer'), _state) + self.service_context.issuer, _state) return request_args, {} def update_service_context(self, resp, key='', **kwargs): @@ -69,12 +69,12 @@ def oidc_pre_construct(self, request_args=None, post_args=None, **kwargs): try: _rt = request_args["response_type"] except KeyError: - _rt = self.service_context.get('behaviour')['response_types'][0] + _rt = self.service_context.behaviour['response_types'][0] request_args["response_type"] = _rt # For OIDC 'openid' is required in scope if 'scope' not in request_args: - request_args['scope'] = self.service_context.get('behaviour').get("scope", ["openid"]) + request_args['scope'] = self.service_context.behaviour.get("scope", ["openid"]) elif 'openid' not in request_args['scope']: request_args['scope'].append('openid') @@ -116,7 +116,7 @@ def get_request_object_signing_alg(self, **kwargs): if not alg: try: - alg = self.service_context.get('behaviour')["request_object_signing_alg"] + alg = self.service_context.behaviour["request_object_signing_alg"] except KeyError: # Use default alg = "RS256" return alg @@ -129,7 +129,7 @@ def store_request_on_file(self, req, **kwargs): :return: The URL the OP should use to access the file """ try: - _webname = self.service_context.get('registration_response')['request_uris'][0] + _webname = self.service_context.registration_response['request_uris'][0] filename = self.service_context.filename_from_webname(_webname) except KeyError: filename, _webname = construct_request_uri(**kwargs) @@ -151,14 +151,15 @@ def construct_request_parameter(self, req, request_method, audience=None, expire _srv_cntx = self.service_context # This is the issuer of the JWT, that is me ! - if kwargs.get('issuer') is None: - kwargs['issuer'] = _srv_cntx.get('client_id') + _issuer = kwargs.get("issuer") + if _issuer is None: + kwargs['issuer'] = _srv_cntx.client_id - if kwargs.get('recv') is None: + if kwargs.get("recv") is None: try: - kwargs['recv'] = _srv_cntx.get('provider_info')['issuer'] + kwargs['recv'] = _srv_cntx.provider_info['issuer'] except KeyError: - kwargs['recv'] = _srv_cntx.get('issuer') + kwargs['recv'] = _srv_cntx.issuer del kwargs['service'] @@ -213,16 +214,16 @@ def gather_verify_arguments(self): """ _ctx = self.service_context kwargs = { - 'iss': _ctx.get('issuer'), + 'iss': _ctx.issuer, 'keyjar': _ctx.keyjar, 'verify': True, 'skew': _ctx.clock_skew } - _client_id = _ctx.get('client_id') + _client_id = _ctx.client_id if _client_id: kwargs['client_id'] = _client_id - _reg_res = _ctx.get('registration_response') + _reg_res = _ctx.registration_response if _reg_res: for attr, param in IDT2REG.items(): try: @@ -235,7 +236,7 @@ def gather_verify_arguments(self): except KeyError: pass - _verify_args = _ctx.get('behaviour').get("verify_args") + _verify_args = _ctx.behaviour.get("verify_args") if _verify_args: kwargs.update(_verify_args) diff --git a/src/oidcservice/oidc/provider_info_discovery.py b/src/oidcservice/oidc/provider_info_discovery.py index ace4da9..d90b2f4 100644 --- a/src/oidcservice/oidc/provider_info_discovery.py +++ b/src/oidcservice/oidc/provider_info_discovery.py @@ -58,13 +58,13 @@ def add_redirect_uris(request_args, service=None, **kwargs): if "redirect_uris" not in request_args: # Callbacks is a dictionary with callback type 'code', 'implicit', # 'form_post' as keys. - _cbs = _context.get('callback') + _cbs = _context.callback if _cbs: # Filter out local additions. _uris = [v for k, v in _cbs.items() if not k.startswith('__')] request_args['redirect_uris'] = _uris else: - request_args['redirect_uris'] = _context.get('redirect_uris') + request_args['redirect_uris'] = _context.redirect_uris return request_args, {} @@ -81,7 +81,7 @@ def __init__(self, service_context, client_authn_factory=None, conf=None): def update_service_context(self, resp, **kwargs): self._update_service_context(resp) - self.match_preferences(resp, self.service_context.get('issuer')) + self.match_preferences(resp, self.service_context.issuer) if 'pre_load_keys' in self.conf and self.conf['pre_load_keys']: _jwks = self.service_context.keyjar.export_jwks_as_json( issuer=resp['issuer']) @@ -103,11 +103,11 @@ def match_preferences(self, pcr=None, issuer=None): """ if not pcr: - pcr = self.service_context.get('provider_info') + pcr = self.service_context.provider_info regreq = oidc.RegistrationRequest - _behaviour = self.service_context.get('behaviour') + _behaviour = self.service_context.behaviour for _pref, _prov in PREFERENCE2PROVIDER.items(): try: @@ -171,5 +171,5 @@ def match_preferences(self, pcr=None, issuer=None): if key not in PREFERENCE2PROVIDER: _behaviour[key] = val - self.service_context.set('behaviour', _behaviour) + self.service_context.behaviour= _behaviour logger.debug('service_context behaviour: {}'.format(_behaviour)) diff --git a/src/oidcservice/oidc/read_registration.py b/src/oidcservice/oidc/read_registration.py index 1fbfb8f..8296c6c 100644 --- a/src/oidcservice/oidc/read_registration.py +++ b/src/oidcservice/oidc/read_registration.py @@ -20,7 +20,7 @@ class RegistrationRead(Service): def get_endpoint(self): try: - return self.service_context.get('registration_response')["registration_client_uri"] + return self.service_context.registration_response["registration_client_uri"] except KeyError: return '' @@ -39,7 +39,7 @@ def get_authn_header(self, request, authn_method, **kwargs): if authn_method == "client_secret_basic": LOGGER.debug("Client authn method: %s", authn_method) headers["Authorization"] = "Bearer {}".format( - self.service_context.get('registration_response')[ + self.service_context.registration_response[ "registration_access_token"] ) diff --git a/src/oidcservice/oidc/refresh_access_token.py b/src/oidcservice/oidc/refresh_access_token.py index ee6e7b2..c433e52 100644 --- a/src/oidcservice/oidc/refresh_access_token.py +++ b/src/oidcservice/oidc/refresh_access_token.py @@ -10,7 +10,6 @@ class RefreshAccessToken(refresh_access_token.RefreshAccessToken): def get_authn_method(self): try: - return self.service_context.get('behaviour')[ - 'token_endpoint_auth_method'] + return self.service_context.behaviour['token_endpoint_auth_method'] except KeyError: return self.default_authn_method diff --git a/src/oidcservice/oidc/registration.py b/src/oidcservice/oidc/registration.py index c4a3f52..1a94101 100644 --- a/src/oidcservice/oidc/registration.py +++ b/src/oidcservice/oidc/registration.py @@ -10,7 +10,6 @@ logger = logging.getLogger(__name__) - rt2gt = { 'code': ['authorization_code'], 'id_token': ['implicit'], @@ -41,7 +40,7 @@ def response_types_to_grant_types(response_types): def add_request_uri(request_args=None, service=None, **kwargs): _context = service.service_context if _context.requests_dir: - _pi = _context.get('provider_info') + _pi = _context.provider_info if _pi: _req = _pi.get('require_request_uri_registration', False) if _req is True: @@ -61,7 +60,7 @@ def add_post_logout_redirect_uris(request_args=None, service=None, **kwargs): """ if "post_logout_redirect_uris" not in request_args: - _uris = service.service_context.register_args.get('post_logout_redirect_uris') + _uris = service.service_context.register_args.get("post_logout_redirect_uris") if _uris: request_args["post_logout_redirect_uris"] = _uris @@ -118,7 +117,7 @@ def add_client_behaviour_preference(self, request_args=None, **kwargs): continue try: - request_args[prop] = self.service_context.get('behaviour')[prop] + request_args[prop] = self.service_context.behaviour[prop] except KeyError: try: request_args[ @@ -144,28 +143,26 @@ def update_service_context(self, resp, key='', **kwargs): if "token_endpoint_auth_method" not in resp: resp["token_endpoint_auth_method"] = "client_secret_basic" - self.service_context.set('registration_response', resp) - _client_id = resp.get('client_id') + self.service_context.registration_response = resp + _client_id = resp.get("client_id") if _client_id: - self.service_context.set('client_id', _client_id) + self.service_context.client_id = _client_id if _client_id not in self.service_context.keyjar: self.service_context.keyjar.import_jwks( self.service_context.keyjar.export_jwks(True, ''), issuer_id=_client_id ) - _client_secret = resp.get('client_secret') + _client_secret = resp.get("client_secret") if _client_secret: - self.service_context.set('client_secret', _client_secret) + self.service_context.client_secret = _client_secret self.service_context.keyjar.add_symmetric('', _client_secret) self.service_context.keyjar.add_symmetric(_client_id, _client_secret) try: - self.service_context.set('client_secret_expires_at', - resp["client_secret_expires_at"]) + self.service_context.client_secret_expires_at = resp["client_secret_expires_at"] except KeyError: pass try: - self.service_context.set('registration_access_token', resp[ - "registration_access_token"]) + self.service_context.registration_access_token = resp["registration_access_token"] except KeyError: pass diff --git a/src/oidcservice/oidc/userinfo.py b/src/oidcservice/oidc/userinfo.py index e417828..03c5fe6 100644 --- a/src/oidcservice/oidc/userinfo.py +++ b/src/oidcservice/oidc/userinfo.py @@ -97,7 +97,9 @@ def post_parse_response(self, response, **kwargs): _info = { "headers": self.get_authn_header( {}, self.default_authn_method, - authn_endpoint=self.endpoint_name), + authn_endpoint=self.endpoint_name, + key=kwargs["state"] + ), "url": spec["endpoint"] } @@ -112,12 +114,13 @@ def gather_verify_arguments(self): """ _ctx = self.service_context kwargs = { - 'client_id': _ctx.get('client_id'), 'iss': _ctx.get('issuer'), + 'client_id': _ctx.client_id, + 'iss': _ctx.issuer, 'keyjar': _ctx.keyjar, 'verify': True, 'skew': _ctx.clock_skew } - _reg_resp = _ctx.get('registration_response') + _reg_resp = _ctx.registration_response if _reg_resp: for attr, param in UI2REG.items(): try: diff --git a/src/oidcservice/oidc/utils.py b/src/oidcservice/oidc/utils.py index 3cf7054..d07269c 100644 --- a/src/oidcservice/oidc/utils.py +++ b/src/oidcservice/oidc/utils.py @@ -20,7 +20,7 @@ def request_object_encryption(msg, service_context, **kwargs): encalg = kwargs["request_object_encryption_alg"] except KeyError: try: - encalg = service_context.get('behaviour')[ + encalg = service_context.behaviour[ "request_object_encryption_alg"] except KeyError: return msg @@ -32,8 +32,7 @@ def request_object_encryption(msg, service_context, **kwargs): encenc = kwargs["request_object_encryption_enc"] except KeyError: try: - encenc = service_context.get('behaviour')[ - "request_object_encryption_enc"] + encenc = service_context.behaviour["request_object_encryption_enc"] except KeyError: raise MissingRequiredAttribute( "No request_object_encryption_enc specified") diff --git a/src/oidcservice/oidc/webfinger.py b/src/oidcservice/oidc/webfinger.py index 1717a3c..c8aa13c 100644 --- a/src/oidcservice/oidc/webfinger.py +++ b/src/oidcservice/oidc/webfinger.py @@ -58,7 +58,7 @@ def update_service_context(self, resp, key='', **kwargs): raise ValueError( 'http link not allowed ({})'.format(_href)) - self.service_context.set('issuer', link['href']) + self.service_context.issuer = link['href'] break return resp diff --git a/src/oidcservice/service.py b/src/oidcservice/service.py index f42ba8e..54f51b8 100644 --- a/src/oidcservice/service.py +++ b/src/oidcservice/service.py @@ -1,19 +1,25 @@ """ The basic Service class upon which all the specific services are built. """ import logging -from typing import Optional, Union +from typing import Optional +from typing import Union from urllib.parse import urlparse from cryptojwt.jwt import JWT +from cryptojwt.utils import qualified_name from oidcmsg.impexp import ImpExp +from oidcmsg.item import DLDict from oidcmsg.message import Message -from oidcmsg.oauth2 import ResponseMessage, is_error_message +from oidcmsg.oauth2 import ResponseMessage +from oidcmsg.oauth2 import is_error_message from oidcservice import util from oidcservice.client_auth import factory as ca_factory from oidcservice.exception import ResponseError -from oidcservice.state_interface import StateInterface -from oidcservice.util import (JOSE_ENCODED, JSON_ENCODED, URL_ENCODED, - get_http_body, get_http_url) +from oidcservice.util import JOSE_ENCODED +from oidcservice.util import JSON_ENCODED +from oidcservice.util import URL_ENCODED +from oidcservice.util import get_http_body +from oidcservice.util import get_http_url __author__ = 'Roland Hedberg' @@ -47,10 +53,11 @@ class Service(ImpExp): 'default_authn_method': None, 'http_method': None, 'request_body_type': None, - 'response_body_type': None, - 'state': StateInterface + 'response_body_type': None } + init_args = ["service_context"] + def __init__(self, service_context, conf=None, client_authn_factory=None, **kwargs): ImpExp.__init__(self) @@ -258,7 +265,7 @@ def get_endpoint(self): if self.endpoint: return self.endpoint - return self.service_context.get('provider_info')[self.endpoint_name] + return self.service_context.provider_info[self.endpoint_name] def get_authn_header(self, request: Union[dict, Message], @@ -360,8 +367,8 @@ def get_request_parameters(self, request_args=None, method="", _info = {'method': method, "request": request} _args = kwargs.copy() - if self.service_context.get('issuer'): - _args['iss'] = self.service_context.get('issuer') + if self.service_context.issuer: + _args['iss'] = self.service_context.issuer # Client authentication by usage of the Authorization HTTP header # or by modifying the request object @@ -433,17 +440,17 @@ def gather_verify_arguments(self): """ kwargs = { - 'iss': self.service_context.get('issuer'), + 'iss': self.service_context.issuer, 'keyjar': self.service_context.keyjar, 'verify': True } - _client_id = self.service_context.get('client_id') + _client_id = self.service_context.client_id if _client_id: kwargs['client_id'] = _client_id if self.service_name == "provider_info": - if self.service_context.get('issuer').startswith("http://"): + if self.service_context.issuer.startswith("http://"): kwargs["allow_http"] = True return kwargs @@ -454,13 +461,13 @@ def _do_jwt(self, info): args['allowed_enc_algs'] = enc_algs['alg'] args['allowed_enc_encs'] = enc_algs['enc'] _jwt = JWT(key_jar=self.service_context.keyjar, **args) - _jwt.iss = self.service_context.get('client_id') + _jwt.iss = self.service_context.client_id return _jwt.unpack(info) def _do_response(self, info, sformat, **kwargs): try: resp = self.response_cls().deserialize( - info, sformat, iss=self.service_context.get('issuer'), **kwargs) + info, sformat, iss=self.service_context.issuer, **kwargs) except Exception as err: resp = None if sformat == 'json': @@ -469,8 +476,7 @@ def _do_response(self, info, sformat, **kwargs): # then two can be. try: resp = self.response_cls().deserialize( - info, 'jwt', iss=self.service_context.get('issuer'), - **kwargs) + info, 'jwt', iss=self.service_context.issuer, **kwargs) except Exception: pass @@ -595,7 +601,7 @@ def init_services(service_definitions, service_context, client_authn_factory=Non :return: A dictionary, with service name as key and the service instance as value. """ - service = {} + service = DLDict() for service_name, service_configuration in service_definitions.items(): try: kwargs = service_configuration['kwargs'] @@ -608,8 +614,10 @@ def init_services(service_definitions, service_context, client_authn_factory=Non }) if isinstance(service_configuration['class'], str): + _value_cls = service_configuration['class'] _srv = util.importer(service_configuration['class'])(**kwargs) else: + _value_cls = qualified_name(service_configuration['class']) _srv = service_configuration['class'](**kwargs) if 'post_functions' in service_configuration: @@ -617,9 +625,6 @@ def init_services(service_definitions, service_context, client_authn_factory=Non if 'pre_functions' in service_configuration: gather_constructors(service_configuration['pre_functions'], _srv.pre_construct) - try: - service[_srv.service_name] = _srv - except AttributeError: - raise ValueError("Could not load '{}'".format(service_name)) + service[_srv.service_name] = _srv return service diff --git a/src/oidcservice/service_context.py b/src/oidcservice/service_context.py index 52af8d6..14a465f 100644 --- a/src/oidcservice/service_context.py +++ b/src/oidcservice/service_context.py @@ -85,26 +85,29 @@ class ServiceContext(OidcContext): """ parameter = OidcContext.parameter.copy() parameter.update({ - "config": None, - "kid": None, - "base_url": None, - "requests_dir": None, - "register_args": None, + "add_on": None, "allow": None, - "client_preferences": None, "args": None, - "add_on": None, - "httpc_params": None, - 'client_secret': None, - 'client_id': None, - 'redirect_uris': None, - 'provider_info': None, + "base_url": None, 'behaviour': None, 'callback': None, - 'issuer': None, + 'client_id': None, + "client_preferences": None, + 'client_secret': None, + "client_secret_expires_at": 0, 'clock_skew': None, - 'verify_args': None, - 'state': StateInterface + "config": None, + "httpc_params": None, + 'issuer': None, + "kid": None, + "post_logout_redirect_uris": [], + 'provider_info': None, + 'redirect_uris': None, + "requests_dir": None, + "register_args": None, + 'registration_response': None, + 'state': StateInterface, + 'verify_args': None }) def __init__(self, base_url="", keyjar=None, config=None, state=None, **kwargs): @@ -119,8 +122,6 @@ def __init__(self, base_url="", keyjar=None, config=None, state=None, **kwargs): self.base_url = base_url # Below so my IDE won't complain - self.requests_dir = '' - self.register_args = {} self.allow = {} self.client_preferences = {} self.args = {} @@ -129,9 +130,14 @@ def __init__(self, base_url="", keyjar=None, config=None, state=None, **kwargs): self.issuer = "" self.client_id = "" self.client_secret = "" + self.client_secret_expires_at = 0 self.behaviour = {} self.provider_info = {} + self.post_logout_redirect_uris = [] self.redirect_uris = [] + self.register_args = {} + self.registration_response = {} + self.requests_dir = '' _def_value = copy.deepcopy(DEFAULT_VALUE) # Dynamic information @@ -210,9 +216,9 @@ def generate_request_uris(self, path): """ _hash = hashlib.sha256() try: - _hash.update(as_bytes(self.get('provider_info')['issuer'])) + _hash.update(as_bytes(self.provider_info['issuer'])) except KeyError: - _hash.update(as_bytes(self.get('issuer'))) + _hash.update(as_bytes(self.issuer)) _hash.update(as_bytes(self.base_url)) if not path.startswith('/'): @@ -256,10 +262,10 @@ def get_sign_alg(self, typ): """ try: - return self.get('behaviour')[CLI_REG_MAP[typ]['sign']] + return self.behaviour[CLI_REG_MAP[typ]['sign']] except KeyError: try: - return self.get('provider_info')[PROVIDER_INFO_MAP[typ]['sign']] + return self.provider_info[PROVIDER_INFO_MAP[typ]['sign']] except (KeyError, TypeError): pass @@ -275,10 +281,10 @@ def get_enc_alg_enc(self, typ): res = {} for attr in ['enc', 'alg']: try: - _alg = self.get('behaviour')[CLI_REG_MAP[typ][attr]] + _alg = self.behaviour[CLI_REG_MAP[typ][attr]] except KeyError: try: - _alg = self.get('provider_info')[PROVIDER_INFO_MAP[typ][attr]] + _alg = self.provider_info[PROVIDER_INFO_MAP[typ][attr]] except KeyError: _alg = None diff --git a/tests/request123456.jwt b/tests/request123456.jwt index d4598ba..98333bc 100644 --- a/tests/request123456.jwt +++ b/tests/request123456.jwt @@ -1 +1 @@ -eyJhbGciOiJSUzI1NiIsImtpZCI6ImFWODBkazlpZG1sbU1YVlBkMUJYV2xGcGIwZFdZVnBHYkRkVVYxSlFWWGRoV0cxVU9HeFNaRkZCYXcifQ.eyJyZXNwb25zZV90eXBlIjogImNvZGUiLCAic3RhdGUiOiAic3RhdGUiLCAicmVkaXJlY3RfdXJpIjogImh0dHBzOi8vZXhhbXBsZS5jb20vY2xpL2F1dGh6X2NiIiwgInNjb3BlIjogIm9wZW5pZCIsICJub25jZSI6ICJyRHlHU2wySFlKOGJaVTZGMWFoRmZoNk5wcUFwS0J3TSIsICJjbGllbnRfaWQiOiAiY2xpZW50X2lkIiwgImlzcyI6ICJjbGllbnRfaWQiLCAiaWF0IjogMTYxNjQ4OTM1OSwgImF1ZCI6IFsiaHR0cHM6Ly9leGFtcGxlLmNvbSJdfQ.H4U0f-xpMnWW79hclzs4KFdSX8lBvMiAxSdUqjIaURNsgScedJDuzVE-Qt7sSAFYvmWYPLOg6gdEoQdnZr168lnedf4YUHVz8N9b4YNl2Eh1aaaYb98mqcCEaqc30NW32FWONFAGtvK4csWpaAj4oPFCARzAxJNllc8ovRq_tVpydtyiEcSJarlXO6cHaLZefyNLL8Kqc3V3kT4pQi8eXsuSFNgZDHDvUleuBG9bUcbXkYZQr2c8bLIvazUNXI--VyaSZptef6StDOMxGfG3K9oNN7hMG2U6XM3p8qyYuWF7jWsJngkE3pilUrtqdNUs2XoC5s185E-Ai7sdsOLUJA \ No newline at end of file +eyJhbGciOiJSUzI1NiIsImtpZCI6ImFWODBkazlpZG1sbU1YVlBkMUJYV2xGcGIwZFdZVnBHYkRkVVYxSlFWWGRoV0cxVU9HeFNaRkZCYXcifQ.eyJyZXNwb25zZV90eXBlIjogImNvZGUiLCAic3RhdGUiOiAic3RhdGUiLCAicmVkaXJlY3RfdXJpIjogImh0dHBzOi8vZXhhbXBsZS5jb20vY2xpL2F1dGh6X2NiIiwgInNjb3BlIjogIm9wZW5pZCIsICJub25jZSI6ICJRQ0FpNTh3ZUc2cnA5T3lXU0J0d2pFQkVmeWZXOUdkbyIsICJjbGllbnRfaWQiOiAiY2xpZW50X2lkIiwgImlzcyI6ICJjbGllbnRfaWQiLCAiaWF0IjogMTYxNjU3MzAxOSwgImF1ZCI6IFsiaHR0cHM6Ly9leGFtcGxlLmNvbSJdfQ.bR5F2IBQT7XBkpvmesTHkeTdNvjreprgDZxXPk_FFwuoM_aXxTW4aSwTSNtBPf1r6LKYgUsGaGUnW4bVeLfTv_K93MeBWMCE0cDU-X1Bs9IB23aglMt7fxdZJvsUprVANXQWK_2HFPTRsOaEHL4bb0tQsxluSaEL-pvgGhmJf_7C5SxdNrWdkVCPJt9PD-tkKy-71dJK196IOkyOC6pOrtTyDFi9H0AJHjrmOiSI4HMrUR09p8itQI8N0nJNY26yDrBgub5IZ4lOC0Q1usnv_FIQLbWbRMf3G5ezqlncbKbVvIGeHa29ngAEJo4-m_wQI55ZX4tsyxe0lUGgtJa8wQ \ No newline at end of file diff --git a/tests/test_01_service_context.py b/tests/test_01_service_context.py index 9d26100..5fa815c 100644 --- a/tests/test_01_service_context.py +++ b/tests/test_01_service_context.py @@ -72,7 +72,7 @@ def verify_alg_support(service_context, alg, usage, typ): :return: True or False """ - supported = service_context.get('provider_info')[ + supported = service_context.provider_info[ "{}_{}_values_supported".format(usage, typ)] if alg in supported: @@ -93,7 +93,7 @@ def create_client_info_instance(self): self.service_context = ServiceContext(config=config) def test_registration_userinfo_sign_enc_algs(self): - self.service_context.set('behaviour', { + self.service_context.behaviour= { "application_type": "web", "redirect_uris": ["https://client.example.org/callback", "https://client.example.org/callback2"], @@ -101,14 +101,14 @@ def test_registration_userinfo_sign_enc_algs(self): "jwks_uri": "https://client.example.org/my_public_keys.jwks", "userinfo_encrypted_response_alg": "RSA1_5", "userinfo_encrypted_response_enc": "A128CBC-HS256" - }) + } assert self.service_context.get_sign_alg('userinfo') is None assert self.service_context.get_enc_alg_enc('userinfo') == { 'alg': 'RSA1_5', 'enc': 'A128CBC-HS256'} def test_registration_request_object_sign_enc_algs(self): - self.service_context.set('behaviour', { + self.service_context.behaviour= { "application_type": "web", "redirect_uris": ["https://client.example.org/callback", "https://client.example.org/callback2"], @@ -117,7 +117,7 @@ def test_registration_request_object_sign_enc_algs(self): "userinfo_encrypted_response_alg": "RSA1_5", "userinfo_encrypted_response_enc": "A128CBC-HS256", "request_object_signing_alg": "RS384" - }) + } res = self.service_context.get_enc_alg_enc('userinfo') # 'sign':'RS256' is an added default @@ -126,7 +126,7 @@ def test_registration_request_object_sign_enc_algs(self): assert res == 'RS384' def test_registration_id_token_sign_enc_algs(self): - self.service_context.set('behaviour', { + self.service_context.behaviour= { "application_type": "web", "redirect_uris": ["https://client.example.org/callback", "https://client.example.org/callback2"], @@ -138,7 +138,7 @@ def test_registration_id_token_sign_enc_algs(self): 'id_token_encrypted_response_alg': 'ECDH-ES', 'id_token_encrypted_response_enc': "A128GCM", 'id_token_signed_response_alg': "ES384", - }) + } res = self.service_context.get_enc_alg_enc('userinfo') # 'sign':'RS256' is an added default @@ -149,7 +149,7 @@ def test_registration_id_token_sign_enc_algs(self): assert res == {'alg': 'ECDH-ES', 'enc': 'A128GCM'} def test_verify_alg_support(self): - self.service_context.set('provider_info', { + self.service_context.provider_info= { "version": "3.0", "issuer": "https://server.example.com", "authorization_endpoint": @@ -200,7 +200,7 @@ def test_verify_alg_support(self): "http://server.example.com/connect/service_documentation.html", "ui_locales_supported": ["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"] - }) + } assert verify_alg_support(self.service_context, 'RS256', 'id_token', 'signing_alg') @@ -215,7 +215,7 @@ def test_verify_alg_support(self): 'token_endpoint_auth', 'signing_alg') def test_verify_requests_uri(self): - self.service_context.set('provider_info', {'issuer': 'https://example.com/'}) + self.service_context.provider_info= {'issuer': 'https://example.com/'} url_list = self.service_context.generate_request_uris('/leading') sp = urlsplit(url_list[0]) p = sp.path.split('/') @@ -224,7 +224,7 @@ def test_verify_requests_uri(self): assert len(p) == 3 # different for different OPs - self.service_context.set('provider_info', {'issuer': 'https://op.example.org/'}) + self.service_context.provider_info= {'issuer': 'https://op.example.org/'} url_list = self.service_context.generate_request_uris('/leading') sp = urlsplit(url_list[0]) np = sp.path.split('/') diff --git a/tests/test_01_service_context_impexp.py b/tests/test_01_service_context_impexp.py index f73caaf..f09d6b8 100644 --- a/tests/test_01_service_context_impexp.py +++ b/tests/test_01_service_context_impexp.py @@ -1,3 +1,4 @@ +import json import os from urllib.parse import urlsplit @@ -76,7 +77,7 @@ def verify_alg_support(service_context, alg, usage, typ): :return: True or False """ - supported = service_context.get('provider_info')[ + supported = service_context.provider_info[ "{}_{}_values_supported".format(usage, typ)] if alg in supported: @@ -114,7 +115,7 @@ def test_registration_userinfo_sign_enc_algs(self): assert srvcntx.get_enc_alg_enc('userinfo') == {'alg': 'RSA1_5', 'enc': 'A128CBC-HS256'} def test_registration_request_object_sign_enc_algs(self): - self.service_context.set('behaviour', { + self.service_context.behaviour= { "application_type": "web", "redirect_uris": ["https://client.example.org/callback", "https://client.example.org/callback2"], @@ -123,7 +124,7 @@ def test_registration_request_object_sign_enc_algs(self): "userinfo_encrypted_response_alg": "RSA1_5", "userinfo_encrypted_response_enc": "A128CBC-HS256", "request_object_signing_alg": "RS384" - }) + } srvcntx = ServiceContext().load( self.service_context.dump(exclude_attributes=["service_context"])) @@ -133,7 +134,7 @@ def test_registration_request_object_sign_enc_algs(self): assert srvcntx.get_sign_alg('request_object') == 'RS384' def test_registration_id_token_sign_enc_algs(self): - self.service_context.set('behaviour', { + self.service_context.behaviour= { "application_type": "web", "redirect_uris": ["https://client.example.org/callback", "https://client.example.org/callback2"], @@ -145,7 +146,7 @@ def test_registration_id_token_sign_enc_algs(self): 'id_token_encrypted_response_alg': 'ECDH-ES', 'id_token_encrypted_response_enc': "A128GCM", 'id_token_signed_response_alg': "ES384", - }) + } srvcntx = ServiceContext().load( self.service_context.dump(exclude_attributes=["service_context"])) @@ -156,7 +157,7 @@ def test_registration_id_token_sign_enc_algs(self): assert srvcntx.get_enc_alg_enc('id_token') == {'alg': 'ECDH-ES', 'enc': 'A128GCM'} def test_verify_alg_support(self): - self.service_context.set('provider_info', { + self.service_context.provider_info= { "version": "3.0", "issuer": "https://server.example.com", "authorization_endpoint": @@ -207,7 +208,7 @@ def test_verify_alg_support(self): "http://server.example.com/connect/service_documentation.html", "ui_locales_supported": ["en-US", "en-GB", "en-CA", "fr-FR", "fr-CA"] - }) + } srvcntx = ServiceContext().load( self.service_context.dump(exclude_attributes=["service_context"])) @@ -220,7 +221,7 @@ def test_verify_alg_support(self): assert verify_alg_support(srvcntx, 'ES256', 'token_endpoint_auth', 'signing_alg') def test_verify_requests_uri(self): - self.service_context.set('provider_info', {'issuer': 'https://example.com/'}) + self.service_context.provider_info= {'issuer': 'https://example.com/'} url_list = self.service_context.generate_request_uris('/leading') sp = urlsplit(url_list[0]) p = sp.path.split('/') @@ -232,7 +233,7 @@ def test_verify_requests_uri(self): self.service_context.dump(exclude_attributes=["service_context"])) # different for different OPs - srvcntx.set('provider_info', {'issuer': 'https://op.example.org/'}) + srvcntx.provider_info= {'issuer': 'https://op.example.org/'} url_list = srvcntx.generate_request_uris('/leading') sp = urlsplit(url_list[0]) np = sp.path.split('/') @@ -259,6 +260,25 @@ def test_import_keys_file(self): # Now there should be 2, the second a RSA key for signing assert len(srvcntx.keyjar.get_issuer_keys('')) == 2 + def test_import_keys_file_json(self): + # Should only be one and that a symmetric key (client_secret) usable + # for signing and encryption + assert len(self.service_context.keyjar.get_issuer_keys('')) == 1 + + file_path = os.path.abspath( + os.path.join(os.path.dirname(__file__), 'salesforce.key')) + + keyspec = {'file': {'rsa': [file_path]}} + self.service_context.import_keys(keyspec) + + _sc_state = self.service_context.dump(exclude_attributes=["service_context"]) + _jsc_state = json.dumps(_sc_state) + _o_state = json.loads(_jsc_state) + srvcntx = ServiceContext().load(_o_state) + + # Now there should be 2, the second a RSA key for signing + assert len(srvcntx.keyjar.get_issuer_keys('')) == 2 + def test_import_keys_url(self): assert len(self.service_context.keyjar.get_issuer_keys('')) == 1 diff --git a/tests/test_07_service_impexp.py b/tests/test_07_service_impexp.py index 02f2bd4..1581cd5 100644 --- a/tests/test_07_service_impexp.py +++ b/tests/test_07_service_impexp.py @@ -1,10 +1,14 @@ +from oidcmsg.item import DLDict +from oidcmsg.oauth2 import Message +from oidcmsg.oauth2 import SINGLE_OPTIONAL_INT +from oidcmsg.oauth2 import SINGLE_OPTIONAL_STRING +from oidcmsg.oauth2 import SINGLE_REQUIRED_STRING import pytest -from oidcmsg.oauth2 import (SINGLE_OPTIONAL_INT, SINGLE_OPTIONAL_STRING, - SINGLE_REQUIRED_STRING, Message) +from oidcservice.oidc import DEFAULT_SERVICES from oidcservice.service import Service +from oidcservice.service import init_services from oidcservice.service_context import ServiceContext -from oidcservice.state_interface import InMemoryStateDataBase, State class DummyMessage(Message): @@ -26,47 +30,37 @@ class DummyService(Service): msg_type = DummyMessage -class TestDummyService(object): +class TestRequest(object): @pytest.fixture(autouse=True) def create_service(self): - service_context = ServiceContext(client_id='client_id', - issuer='https://www.example.org/as') - self.service = DummyService(service_context) + self.service_context = ServiceContext(None) + self.service = Service(self.service_context, client_authn_method=None) def test_construct(self): - _srv = Service(self.service.dump(exclude_attributes=["service"])) + _srv = Service(service_context=self.service_context).load( + self.service.dump(exclude_attributes=["service_context"])) + req_args = {'foo': 'bar'} _req = _srv.construct(request_args=req_args) assert isinstance(_req, Message) assert list(_req.keys()) == ['foo'] - def test_construct_service_context(self): - _srv = Service(self.service.dump(exclude_attributes=["service"])) - req_args = {'foo': 'bar', 'req_str': 'some string'} - _req = _srv.construct(request_args=req_args) - assert isinstance(_req, Message) - assert set(_req.keys()) == {'foo', 'req_str'} - def test_get_request_parameters(self): - _srv = Service(self.service.dump(exclude_attributes=["service"])) - req_args = {'foo': 'bar', 'req_str': 'some string'} - _srv.endpoint = 'https://example.com/authorize' - _info = _srv.get_request_parameters(request_args=req_args) - assert set(_info.keys()) == {'url', 'method', "request"} - msg = DummyMessage().from_urlencoded(_srv.get_urlinfo(_info['url'])) - assert msg.to_dict() == {'foo': 'bar', 'req_str': 'some string'} +def test_init_service_imp_exp_dict(): + service_context = ServiceContext(None) + service = init_services(DEFAULT_SERVICES, service_context, + client_authn_factory=None) + assert set(service.keys()) == {'provider_info', 'registration', 'authorization', + 'accesstoken', 'refresh_token', 'userinfo'} + auth_service = service["authorization"] + auth_service.default_authn_method = "foobar" + dump = service.dump(exclude_attributes=["service_context"]) + service_copy = DLDict() + service_copy.load(dump, init_args={"service_context": service_context}) -class TestRequest(object): - @pytest.fixture(autouse=True) - def create_service(self): - service_context = ServiceContext(None) - self.service = Service(service_context, client_authn_method=None) + assert set(service_copy.keys()) == {'provider_info', 'registration', 'authorization', + 'accesstoken', 'refresh_token', 'userinfo'} + auth_service_copy = service_copy["authorization"] - def test_construct(self): - _srv = Service(self.service.dump(exclude_attributes=["service"])) - - req_args = {'foo': 'bar'} - _req = _srv.construct(request_args=req_args) - assert isinstance(_req, Message) - assert list(_req.keys()) == ['foo'] + assert auth_service_copy.default_authn_method == "foobar" diff --git a/tests/test_09_client_auth.py b/tests/test_09_client_auth.py index e1d99db..09aff5f 100755 --- a/tests/test_09_client_auth.py +++ b/tests/test_09_client_auth.py @@ -2,26 +2,38 @@ import os from urllib.parse import quote_plus -import pytest +from cryptojwt.exception import MissingKey +from cryptojwt.exception import UnsupportedAlgorithm +from cryptojwt.jwk.rsa import new_rsa_key from cryptojwt.jws.jws import JWS +from cryptojwt.jws.jws import factory from cryptojwt.jwt import JWT from cryptojwt.key_bundle import KeyBundle from cryptojwt.key_jar import KeyJar from oidcmsg.message import Message -from oidcmsg.oauth2 import (AccessTokenRequest, AccessTokenResponse, - AuthorizationRequest, AuthorizationResponse, - CCAccessTokenRequest, ResourceRequest) +from oidcmsg.oauth2 import AccessTokenRequest +from oidcmsg.oauth2 import AccessTokenResponse +from oidcmsg.oauth2 import AuthorizationRequest +from oidcmsg.oauth2 import AuthorizationResponse +from oidcmsg.oauth2 import CCAccessTokenRequest +from oidcmsg.oauth2 import ResourceRequest +import pytest from oidcservice import JWT_BEARER -from oidcservice.client_auth import (BearerBody, BearerHeader, - ClientSecretBasic, ClientSecretJWT, - ClientSecretPost, PrivateKeyJWT, - assertion_jwt, valid_service_context) +from oidcservice.client_auth import AuthnFailure +from oidcservice.client_auth import BearerBody +from oidcservice.client_auth import BearerHeader +from oidcservice.client_auth import ClientSecretBasic +from oidcservice.client_auth import ClientSecretJWT +from oidcservice.client_auth import ClientSecretPost +from oidcservice.client_auth import PrivateKeyJWT +from oidcservice.client_auth import assertion_jwt +from oidcservice.client_auth import bearer_auth +from oidcservice.client_auth import valid_service_context from oidcservice.oidc import DEFAULT_SERVICES from oidcservice.service import init_services from oidcservice.service_context import ServiceContext from oidcservice.service_factory import service_factory -from oidcservice.state_interface import InMemoryStateDataBase, State BASE_PATH = os.path.abspath(os.path.dirname(__file__)) CLIENT_ID = "A" @@ -38,13 +50,13 @@ def _eq(l1, l2): def get_service_context(): service_context = ServiceContext(keyjar=None, config=CLIENT_CONF) - # service_context.set('client_secret', "white boarding pass") + # service_context.client_secret= "white boarding pass" return service_context def get_service(): service_context = ServiceContext(keyjar=None, config=CLIENT_CONF) - # service_context.set('client_secret', "white boarding pass") + # service_context.client_secret= "white boarding pass" service = service_factory('AccessToken', ['oidc'], service_context=service_context) return service @@ -57,7 +69,7 @@ def services(): # auth_response = AuthorizationResponse(access_token="token", # state='ABCDE').to_json() services = init_services(DEFAULT_SERVICES, get_service_context()) - # db.set('ABCDE', State(iss='Issuer', auth_request=auth_request, + # db.ABCDE', State(iss='Issuer', auth_request=auth_request, # auth_response=auth_response).to_json()) return services @@ -66,12 +78,12 @@ def test_quote(): csb = ClientSecretBasic() http_args = csb.construct( Message(), - password='MKEM/A7Pkn7JuU0LAcxyHVKvwdczsugaPU0BieLb4CbQAgQj+ypcanFOCb0/FA5h' , + password='MKEM/A7Pkn7JuU0LAcxyHVKvwdczsugaPU0BieLb4CbQAgQj+ypcanFOCb0/FA5h', user='796d8fae-a42f-4e4f-ab25-d6205b6d4fa2') assert http_args['headers'][ - 'Authorization'] == 'Basic Nzk2ZDhmYWUtYTQyZi00ZTRmLWFiMjUtZDYyMDViNmQ0ZmEyOk1LRU0lMkZBN1BrbjdKdVUwTEFjeHlIVkt2d2RjenN1Z2FQVTBCaWVMYjRDYlFBZ1FqJTJCeXBjYW5GT0NiMCUyRkZBNWg=' - + 'Authorization'] == 'Basic ' \ + 'Nzk2ZDhmYWUtYTQyZi00ZTRmLWFiMjUtZDYyMDViNmQ0ZmEyOk1LRU0lMkZBN1BrbjdKdVUwTEFjeHlIVkt2d2RjenN1Z2FQVTBCaWVMYjRDYlFBZ1FqJTJCeXBjYW5GT0NiMCUyRkZBNWg=' class TestClientSecretBasic(object): @@ -249,6 +261,28 @@ def test_construct(self, services): assert request["client_secret"] == "another" assert http_args is None + def test_modify_1(self, services): + token_service = services['accesstoken'] + request = token_service.construct(redirect_uri="http://example.com", + state='ABCDE') + csp = ClientSecretPost() + # client secret not in request or kwargs + del request["client_secret"] + http_args = csp.construct(request, service=token_service) + assert "client_secret" in request + + def test_modify_2(self, services): + token_service = services['accesstoken'] + request = token_service.construct(redirect_uri="http://example.com", + state='ABCDE') + csp = ClientSecretPost() + # client secret not in request or kwargs + del request["client_secret"] + token_service.service_context.client_secret = "" + # this will fail + with pytest.raises(AuthnFailure): + http_args = csp.construct(request, service=token_service) + class TestPrivateKeyJWT(object): def test_construct(self, services): @@ -260,11 +294,11 @@ def test_construct(self, services): key.add_kid() _service.service_context.keyjar.add_kb('', kb_rsa) - _service.service_context.set('provider_info', { + _service.service_context.provider_info = { 'issuer': 'https://example.com/', - 'token_endpoint': "https://example.com/token"}) - _service.service_context.set("registration_response", { - 'token_endpoint_auth_signing_alg': 'RS256'}) + 'token_endpoint': "https://example.com/token"} + _service.service_context.registration_response = { + 'token_endpoint_auth_signing_alg': 'RS256'} services['accesstoken'].endpoint = "https://example.com/token" request = AccessTokenRequest() @@ -274,12 +308,12 @@ def test_construct(self, services): cas = request["client_assertion"] _kj = KeyJar() - _kj.add_kb(_service.service_context.get('client_id'), kb_rsa) + _kj.add_kb(_service.service_context.client_id, kb_rsa) jso = JWT(key_jar=_kj).unpack(cas) assert _eq(jso.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) # assert _jwt.headers == {'alg': 'RS256'} assert jso['aud'] == [ - _service.service_context.get('provider_info')['token_endpoint']] + _service.service_context.provider_info['token_endpoint']] def test_construct_client_assertion(self, services): _service = services['accesstoken'] @@ -290,7 +324,7 @@ def test_construct_client_assertion(self, services): request = AccessTokenRequest() pkj = PrivateKeyJWT() _ca = assertion_jwt( - _service.service_context.get('client_id'), kb_rsa.get('RSA'), + _service.service_context.client_id, kb_rsa.get('RSA'), "https://example.com/token", 'RS256') http_args = pkj.construct(request, client_assertion=_ca) assert http_args == {} @@ -303,12 +337,12 @@ def test_client_secret_jwt(self, services): _service_context = services['accesstoken'].service_context _service_context.token_endpoint = "https://example.com/token" - _service_context.set('provider_info', { + _service_context.provider_info = { 'issuer': 'https://example.com/', - 'token_endpoint': "https://example.com/token"}) + 'token_endpoint': "https://example.com/token"} - _service_context.set("registration_response", { - 'token_endpoint_auth_signing_alg': "HS256"}) + _service_context.registration_response = { + 'token_endpoint_auth_signing_alg': "HS256"} csj = ClientSecretJWT() request = AccessTokenRequest() @@ -319,28 +353,113 @@ def test_client_secret_jwt(self, services): cas = request["client_assertion"] _kj = KeyJar() - _kj.add_symmetric(_service_context.get('client_id'), - _service_context.get('client_secret'), - ['sig']) + _kj.add_symmetric(_service_context.client_id, _service_context.client_secret, ['sig']) jso = JWT(key_jar=_kj, sign_alg='HS256').unpack(cas) assert _eq(jso.keys(), ["aud", "iss", "sub", "exp", "iat", 'jti']) _rj = JWS(alg='HS256') info = _rj.verify_compact( - cas, _kj.get_signing_key(issuer_id=_service_context.get('client_id'))) + cas, _kj.get_signing_key(issuer_id=_service_context.client_id)) assert _eq(info.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) - assert info['aud'] == [_service_context.get('provider_info')[ - 'token_endpoint']] + assert info['aud'] == [_service_context.provider_info['token_endpoint']] + + def test_get_key_by_kid(self, services): + _service_context = services['accesstoken'].service_context + _service_context.token_endpoint = "https://example.com/token" + + _service_context.provider_info = { + 'issuer': 'https://example.com/', + 'token_endpoint': "https://example.com/token"} + + _service_context.registration_response = { + 'token_endpoint_auth_signing_alg': "HS256"} + + csj = ClientSecretJWT() + request = AccessTokenRequest() + + # get a kid + _keys = _service_context.keyjar.get_issuer_keys("") + kid = _keys[0].kid + csj.construct(request, service=services['accesstoken'], authn_endpoint='token_endpoint', + kid=kid) + assert "client_assertion" in request + + def test_get_key_by_kid_fail(self, services): + _service_context = services['accesstoken'].service_context + _service_context.token_endpoint = "https://example.com/token" + + _service_context.provider_info = { + 'issuer': 'https://example.com/', + 'token_endpoint': "https://example.com/token"} + + _service_context.registration_response = { + 'token_endpoint_auth_signing_alg': "HS256"} + + csj = ClientSecretJWT() + request = AccessTokenRequest() + + # get a kid + kid = "abcdefgh" + with pytest.raises(MissingKey): + csj.construct(request, service=services['accesstoken'], + authn_endpoint='token_endpoint', kid=kid) + + def test_get_audience_and_algorithm_default_alg(self, services): + _service_context = services['accesstoken'].service_context + _service_context.token_endpoint = "https://example.com/token" + + _service_context.provider_info = { + 'issuer': 'https://example.com/', + 'token_endpoint': "https://example.com/token"} + + _service_context.registration_response = { + 'token_endpoint_auth_signing_alg': "HS256"} + + csj = ClientSecretJWT() + request = AccessTokenRequest() + + _service_context.registration_response = {} + + # Add a RSA key to be able to handle default + _kb = KeyBundle() + _rsa_key = new_rsa_key() + _kb.append(_rsa_key) + _service_context.keyjar.add_kb("", _kb) + # Since I have a RSA key this doesn't fail + csj.construct(request, service=services['accesstoken'], authn_endpoint='token_endpoint') + + _jws = factory(request["client_assertion"]) + assert _jws.jwt.headers["alg"] == "RS256" + assert _jws.jwt.headers["kid"] == _rsa_key.kid + + # By client preferences + request = AccessTokenRequest() + _service_context.client_preferences = {"token_endpoint_auth_signing_alg": "RS512"} + csj.construct(request, service=services['accesstoken'], authn_endpoint='token_endpoint') + + _jws = factory(request["client_assertion"]) + assert _jws.jwt.headers["alg"] == "RS512" + assert _jws.jwt.headers["kid"] == _rsa_key.kid + + # Use provider information is everything else fails + request = AccessTokenRequest() + _service_context.client_preferences = {} + _service_context.provider_info["token_endpoint_auth_signing_alg_values_supported"] = ["ES256", "RS256"] + csj.construct(request, service=services['accesstoken'], authn_endpoint='token_endpoint') + + _jws = factory(request["client_assertion"]) + # Should be RS256 since I have no key for ES256 + assert _jws.jwt.headers["alg"] == "RS256" + assert _jws.jwt.headers["kid"] == _rsa_key.kid class TestClientSecretJWT_UI(object): def test_client_secret_jwt(self, services): _service_context = services['accesstoken'].service_context _service_context.token_endpoint = "https://example.com/token" - _service_context.set('provider_info', - {'issuer': 'https://example.com/', - 'token_endpoint': "https://example.com/token"}) + _service_context.provider_info = {'issuer': 'https://example.com/', + 'token_endpoint': "https://example.com/token"} csj = ClientSecretJWT() request = AccessTokenRequest() @@ -352,8 +471,8 @@ def test_client_secret_jwt(self, services): cas = request["client_assertion"] _kj = KeyJar() - _kj.add_symmetric(_service_context.get('client_id'), - _service_context.get('client_secret'), + _kj.add_symmetric(_service_context.client_id, + _service_context.client_secret, usage=['sig']) jso = JWT(key_jar=_kj, sign_alg='HS256').unpack(cas) assert _eq(jso.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) @@ -361,10 +480,10 @@ def test_client_secret_jwt(self, services): _rj = JWS(alg='HS256') info = _rj.verify_compact( cas, - _kj.get_signing_key(issuer_id=_service_context.get('client_id'))) + _kj.get_signing_key(issuer_id=_service_context.client_id)) assert _eq(info.keys(), ["aud", "iss", "sub", "jti", "exp", "iat"]) - assert info['aud'] == [_service_context.get('provider_info')['issuer']] + assert info['aud'] == [_service_context.provider_info['issuer']] class TestValidClientInfo(object): @@ -374,15 +493,30 @@ def test_valid_service_context(self, services): # Expiration time missing or 0, client_secret never expires # service_context.client_secret_expires_at assert valid_service_context(_service_context, _now) - _service_context.set('client_secret_expires_at', 0) assert valid_service_context(_service_context, _now) # Expired secret - _service_context.set('client_secret_expires_at', 1) + _service_context.client_secret_expires_at = 1 assert valid_service_context(_service_context, _now) is not True - _service_context.set('client_secret_expires_at', 123455) + _service_context.client_secret_expires_at = 123455 assert valid_service_context(_service_context, _now) is not True # Valid secret - _service_context.set('client_secret_expires_at', 123460) - assert valid_service_context({'client_secret_expires_at': 123460}, _now) + _service_context.client_secret_expires_at = 123460 + assert valid_service_context(_service_context, _now) + + +def test_bearer_auth(): + request = ResourceRequest(access_token="12345678") + authn = "" + assert bearer_auth(request, authn) == "12345678" + + request = ResourceRequest() + authn = "Bearer abcdefghijklm" + assert bearer_auth(request, authn) == "abcdefghijklm" + + request = ResourceRequest() + authn = "" + with pytest.raises(ValueError): + bearer_auth(request, authn) + diff --git a/tests/test_10_oauth2_service.py b/tests/test_10_oauth2_service.py index 85ced1c..0eba519 100644 --- a/tests/test_10_oauth2_service.py +++ b/tests/test_10_oauth2_service.py @@ -1,12 +1,13 @@ +from oidcmsg.oauth2 import AccessTokenRequest +from oidcmsg.oauth2 import AccessTokenResponse +from oidcmsg.oauth2 import AuthorizationRequest +from oidcmsg.oauth2 import AuthorizationResponse +from oidcmsg.oauth2 import Message import pytest -from oidcmsg.oauth2 import (AccessTokenRequest, AccessTokenResponse, - AuthorizationRequest, AuthorizationResponse, - Message) from oidcservice.service import Service from oidcservice.service_context import ServiceContext from oidcservice.service_factory import service_factory -from oidcservice.state_interface import InMemoryStateDataBase, State class Response(object): @@ -42,7 +43,7 @@ def test_construct(self): assert set(_req.keys()) == {'client_id', 'redirect_uri', 'foo', 'state'} assert self.service.service_context.state.get_state('state') _item = self.service.service_context.state.get_item(AuthorizationRequest, 'auth_request', - 'state') + 'state') assert _item.to_dict() == { 'foo': 'bar', 'redirect_uri': 'https://example.com/cli/authz_cb', 'state': 'state', 'client_id': 'client_id' @@ -63,7 +64,7 @@ def test_get_request_parameters(self): } def test_request_init(self): - req_args = {'response_type': 'code', 'state': 'state'} + req_args = {'response_type': 'code', 'state': "state"} self.service.endpoint = 'https://example.com/authorize' _info = self.service.get_request_parameters(request_args=req_args) assert set(_info.keys()) == {'url', 'method', 'request'} @@ -75,6 +76,22 @@ def test_request_init(self): 'response_type': 'code', 'state': 'state' } + def test_response(self): + _state = "today" + req_args = {'response_type': 'code', 'state': _state} + self.service.endpoint = 'https://example.com/authorize' + _info = self.service.get_request_parameters(request_args=req_args) + assert set(_info.keys()) == {'url', 'method', 'request'} + msg = AuthorizationRequest().from_urlencoded( + self.service.get_urlinfo(_info['url'])) + self.service.service_context.state.store_item(msg, "auth_request", _state) + + resp1 = AuthorizationResponse(code="auth_grant", state=_state) + response = self.service.parse_response( + resp1.to_urlencoded(), "urlencoded", state=_state) + self.service.update_service_context(response, key=_state) + assert self.service.service_context.state.get_state(_state) + class TestAccessTokenRequest(object): @pytest.fixture(autouse=True) @@ -85,7 +102,7 @@ def create_service(self): 'redirect_uris': ['https://example.com/cli/authz_cb'] } service_context = ServiceContext(config=client_config) - service_context.set('redirect_uris', ['https://example.com/cli/authz_cb']) + service_context.redirect_uris= ['https://example.com/cli/authz_cb'] self.service = service_factory('AccessToken', ['oauth2'], service_context=service_context) auth_request = AuthorizationRequest( redirect_uri='https://example.com/cli/authz_cb', @@ -238,12 +255,14 @@ def test_access_token_srv_conf(): service_context=service_context, conf={'default_authn_method': 'client_secret_post'}) + _state_val = service_context.state.create_state(service_context.issuer) auth_request = AuthorizationRequest( - redirect_uri='https://example.com/cli/authz_cb', state='state') + redirect_uri='https://example.com/cli/authz_cb', state=_state_val) + _state_interface = service.service_context.state + _state_interface.store_item(auth_request, "auth_request", _state_val) + auth_response = AuthorizationResponse(code='access_code') - _state = service.service_context.state - _state.store_item(auth_request, "auth_request", 'state') - _state.store_item(auth_response, "auth_response", 'state') + _state_interface.store_item(auth_response, "auth_response", _state_val) req_args = { 'redirect_uri': 'https://example.com/cli/authz_cb', @@ -251,7 +270,7 @@ def test_access_token_srv_conf(): } service.endpoint = 'https://example.com/authorize' _info = service.get_request_parameters(request_args=req_args, - state='state') + state=_state_val) assert _info msg = AccessTokenRequest().from_urlencoded(service.get_urlinfo( diff --git a/tests/test_13_oic_service.py b/tests/test_13_oic_service.py index 4563631..4203493 100644 --- a/tests/test_13_oic_service.py +++ b/tests/test_13_oic_service.py @@ -14,6 +14,7 @@ verified_claim_name) from oidcmsg.oidc.session import (CheckIDRequest, CheckSessionRequest, EndSessionRequest) +import responses from oidcservice.exception import ParameterError from oidcservice.oidc.registration import (add_jwks_uri_or_jwks, @@ -49,8 +50,7 @@ def __init__(self, status_code, text, headers=None): ISS_KEY.import_jwks_as_json(open('{}/pub_client.jwks'.format(_dirname)).read(), 'client_id') -CLI_KEY.import_jwks_as_json(open('{}/pub_iss.jwks'.format(_dirname)).read(), - ISS) +CLI_KEY.import_jwks_as_json(open('{}/pub_iss.jwks'.format(_dirname)).read(), ISS) # def test_request_factory(): @@ -67,7 +67,7 @@ def create_request(self): 'redirect_uris': ['https://example.com/cli/authz_cb'] } service_context = ServiceContext(keyjar=CLI_KEY, config=client_config) - service_context.set('issuer', 'https://example.com') + service_context.issuer= 'https://example.com' self.service = service_factory('Authorization', ['oidc'], service_context=service_context) @@ -161,10 +161,10 @@ def test_request_param(self): assert os.path.isfile(os.path.join(_dirname, 'request123456.jwt')) - self.service.service_context.set('registration_response', { + self.service.service_context.registration_response= { 'redirect_uris': ['https://example.com/cb'], 'request_uris': ['https://example.com/request123456.jwt'] - }) + } self.service.service_context.base_url = 'https://example.com/' _info = self.service.get_request_parameters(request_args=req_args, request_method='reference') @@ -243,7 +243,7 @@ def test_allow_unsigned_idtoken(self, allow_sign_alg_none): idt = JWT(ISS_KEY, iss=ISS, lifetime=3600, sign_alg='none') payload = {'sub': '123456789', 'aud': ['client_id']} _idt = idt.pack(payload) - self.service.service_context.get('behaviour')["verify_args"] = { + self.service.service_context.behaviour["verify_args"] = { "allow_sign_alg_none": allow_sign_alg_none } resp = AuthorizationResponse(state='state', code='code', id_token=_idt) @@ -500,18 +500,24 @@ def test_post_parse(self): "A128GCM", "A192GCM", "A256GCM"], "acr_values_supported": ["PASSWORD"], "issuer": OP_BASEURL, - "jwks_uri": "{}/static/jwks_tE2iLbOAqXhe8bqh.json".format( - OP_BASEURL), + "jwks_uri": "{}/static/jwks_tE2iLbOAqXhe8bqh.json".format(OP_BASEURL), "authorization_endpoint": "{}/authorization".format(OP_BASEURL), "token_endpoint": "{}/token".format(OP_BASEURL), "userinfo_endpoint": "{}/userinfo".format(OP_BASEURL), "registration_endpoint": "{}/registration".format(OP_BASEURL), "end_session_endpoint": "{}/end_session".format(OP_BASEURL) } - assert self.service.service_context.get('behaviour') == {} + assert self.service.service_context.behaviour == {} resp = self.service.post_parse_response(provider_info_response) - self.service.update_service_context(resp) - assert self.service.service_context.get('behaviour') == { + + iss_jwks = ISS_KEY.export_jwks_as_json(issuer_id=ISS) + with responses.RequestsMock() as rsps: + rsps.add("GET", resp["jwks_uri"], + body=iss_jwks, status=200) + + self.service.update_service_context(resp) + + assert self.service.service_context.behaviour == { 'token_endpoint_auth_method': 'client_secret_basic', 'response_types': ['code'], 'application_type': 'web', @@ -529,18 +535,24 @@ def test_post_parse_2(self): "client_secret_post", "client_secret_basic", "client_secret_jwt", "private_key_jwt"], "issuer": OP_BASEURL, - "jwks_uri": "{}/static/jwks_tE2iLbOAqXhe8bqh.json".format( - OP_BASEURL), + "jwks_uri": "{}/static/jwks_tE2iLbOAqXhe8bqh.json".format(OP_BASEURL), "authorization_endpoint": "{}/authorization".format(OP_BASEURL), "token_endpoint": "{}/token".format(OP_BASEURL), "userinfo_endpoint": "{}/userinfo".format(OP_BASEURL), "registration_endpoint": "{}/registration".format(OP_BASEURL), "end_session_endpoint": "{}/end_session".format(OP_BASEURL) } - assert self.service.service_context.get('behaviour') == {} + assert self.service.service_context.behaviour == {} resp = self.service.post_parse_response(provider_info_response) - self.service.update_service_context(resp) - assert self.service.service_context.get('behaviour') == { + + iss_jwks = ISS_KEY.export_jwks_as_json(issuer_id=ISS) + with responses.RequestsMock() as rsps: + rsps.add("GET", resp["jwks_uri"], + body=iss_jwks, status=200) + + self.service.update_service_context(resp) + + assert self.service.service_context.behaviour == { 'token_endpoint_auth_method': 'client_secret_basic', 'response_types': ['code'], 'application_type': 'web', @@ -600,9 +612,9 @@ def test_config_with_post_logout(self): assert 'post_logout_redirect_uris' in _req def test_config_with_required_request_uri(self): - _pi = self.service.service_context.get('provider_info') + _pi = self.service.service_context.provider_info _pi['require_request_uri_registration'] = True - self.service.service_context.set('provider_info', _pi) + self.service.service_context.provider_info= _pi _req = self.service.construct() assert isinstance(_req, RegistrationRequest) assert len(_req) == 5 @@ -621,11 +633,11 @@ def create_request(self): } service_context = ServiceContext(config=client_config) service_context.keyjar = CLI_KEY - service_context.set('behaviour', { + service_context.behaviour= { 'userinfo_signed_response_alg': 'RS256', "userinfo_encrypted_response_alg": "RSA-OAEP", "userinfo_encrypted_response_enc": "A256GCM" - }) + } self.service = service_factory('UserInfo', ['oidc'], service_context=service_context) diff --git a/tests/test_15_oic_utils.py b/tests/test_15_oic_utils.py index 09c2b4f..76f9327 100644 --- a/tests/test_15_oic_utils.py +++ b/tests/test_15_oic_utils.py @@ -27,10 +27,10 @@ def test_request_object_encryption(): 'client_secret': 'abcdefghijklmnop', } service_context = ServiceContext(keyjar=KEYJAR, config=conf) - _behav = service_context.get('behaviour') + _behav = service_context.behaviour _behav["request_object_encryption_alg"] = 'RSA1_5' _behav["request_object_encryption_enc"] = "A128CBC-HS256" - service_context.set('behaviour', _behav) + service_context.behaviour= _behav _jwe = request_object_encryption(msg.to_json(), service_context, target=RECEIVER) assert _jwe diff --git a/tests/test_20_conversation.py b/tests/test_20_conversation.py index a39b2a8..f28c358 100644 --- a/tests/test_20_conversation.py +++ b/tests/test_20_conversation.py @@ -155,7 +155,7 @@ def test_conversation(): href='https://example.org/op')] service['webfinger'].update_service_context(resp=response) - service_context.set('issuer', OP_BASEURL) + service_context.issuer= OP_BASEURL # =================== Provider info discovery ==================== @@ -255,7 +255,7 @@ def test_conversation(): assert isinstance(resp, ProviderConfigurationResponse) service['provider_info'].update_service_context(resp) - _pi = service_context.get('provider_info') + _pi = service_context.provider_info assert _pi['issuer'] == OP_BASEURL assert _pi['authorization_endpoint'] == 'https://example.org/op/authorization' assert _pi['registration_endpoint'] == 'https://example.org/op/registration' @@ -299,9 +299,9 @@ def test_conversation(): op_client_registration_response) service['registration'].update_service_context(response) - assert service_context.get('client_id') == 'zls2qhN1jO6A' - assert service_context.get('client_secret') == 'c8434f28cf9375d9a7' - assert set(service_context.get('registration_response').keys()) == { + assert service_context.client_id == 'zls2qhN1jO6A' + assert service_context.client_secret == 'c8434f28cf9375d9a7' + assert set(service_context.registration_response.keys()) == { 'client_secret_expires_at', 'contacts', 'client_id', 'token_endpoint_auth_method', 'redirect_uris', 'response_types', 'client_id_issued_at', 'client_secret', 'application_type', @@ -346,7 +346,7 @@ def test_conversation(): request_args = { 'state': STATE, - 'redirect_uri': service_context.get('redirect_uris')[0] + 'redirect_uri': service_context.redirect_uris[0] } info = service['accesstoken'].get_request_parameters( @@ -385,7 +385,7 @@ def test_conversation(): "id_token": _jws } - service_context.set('issuer', OP_BASEURL) + service_context.issuer= OP_BASEURL _resp = service['accesstoken'].parse_response(json.dumps(_resp), state=STATE) diff --git a/tests/test_21_pushed_auth.py b/tests/test_21_pushed_auth.py index ac64ea7..1e9a46b 100644 --- a/tests/test_21_pushed_auth.py +++ b/tests/test_21_pushed_auth.py @@ -59,9 +59,9 @@ def create_client(self): do_add_ons(config['add_ons'], self.service) service_context.service = self.service - service_context.set('provider_info', { + service_context.provider_info= { "pushed_authorization_request_endpoint": "https://as.example.com/push" - }) + } def test_authorization(self): auth_service = self.service["authorization"] @@ -72,7 +72,7 @@ def test_authorization(self): "expires_in": 3600 } rsps.add("GET", - auth_service.service_context.get('provider_info')[ + auth_service.service_context.provider_info[ "pushed_authorization_request_endpoint"], body=json.dumps(_resp), status=200)