").append(x.parseHTML(e)).find(r):e)}).complete(n&&function(e,t){s.each(n,o||[e.responseText,t,e])}),this},x.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){x.fn[t]=function(e){return this.on(t,e)}}),x.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ut,type:"GET",isLocal:Kt.test(Xt[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":sn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":x.parseJSON,"text xml":x.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?cn(cn(e,x.ajaxSettings),t):cn(x.ajaxSettings,e)},ajaxPrefilter:un(rn),ajaxTransport:un(on),ajax:function(e,t){"object"==typeof e&&(t=e,e=undefined),t=t||{};var n,r,i,o,s,a,u,l,c=x.ajaxSetup({},t),p=c.context||c,f=c.context&&(p.nodeType||p.jquery)?x(p):x.event,h=x.Deferred(),d=x.Callbacks("once memory"),g=c.statusCode||{},m={},y={},v=0,b="canceled",T={readyState:0,getResponseHeader:function(e){var t;if(2===v){if(!o){o={};while(t=Qt.exec(i))o[t[1].toLowerCase()]=t[2]}t=o[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===v?i:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return v||(e=y[n]=y[n]||e,m[e]=t),this},overrideMimeType:function(e){return v||(c.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>v)for(t in e)g[t]=[g[t],e[t]];else T.always(e[T.status]);return this},abort:function(e){var t=e||b;return n&&n.abort(t),k(0,t),this}};if(h.promise(T).complete=d.add,T.success=T.done,T.error=T.fail,c.url=((e||c.url||Ut)+"").replace(Gt,"").replace(en,Xt[1]+"//"),c.type=t.method||t.type||c.method||c.type,c.dataTypes=x.trim(c.dataType||"*").toLowerCase().match(w)||[""],null==c.crossDomain&&(a=tn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===Xt[1]&&a[2]===Xt[2]&&(a[3]||("http:"===a[1]?"80":"443"))===(Xt[3]||("http:"===Xt[1]?"80":"443")))),c.data&&c.processData&&"string"!=typeof c.data&&(c.data=x.param(c.data,c.traditional)),ln(rn,c,t,T),2===v)return T;u=c.global,u&&0===x.active++&&x.event.trigger("ajaxStart"),c.type=c.type.toUpperCase(),c.hasContent=!Zt.test(c.type),r=c.url,c.hasContent||(c.data&&(r=c.url+=(Vt.test(r)?"&":"?")+c.data,delete c.data),c.cache===!1&&(c.url=Jt.test(r)?r.replace(Jt,"$1_="+Yt++):r+(Vt.test(r)?"&":"?")+"_="+Yt++)),c.ifModified&&(x.lastModified[r]&&T.setRequestHeader("If-Modified-Since",x.lastModified[r]),x.etag[r]&&T.setRequestHeader("If-None-Match",x.etag[r])),(c.data&&c.hasContent&&c.contentType!==!1||t.contentType)&&T.setRequestHeader("Content-Type",c.contentType),T.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+("*"!==c.dataTypes[0]?", "+sn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)T.setRequestHeader(l,c.headers[l]);if(c.beforeSend&&(c.beforeSend.call(p,T,c)===!1||2===v))return T.abort();b="abort";for(l in{success:1,error:1,complete:1})T[l](c[l]);if(n=ln(on,c,t,T)){T.readyState=1,u&&f.trigger("ajaxSend",[T,c]),c.async&&c.timeout>0&&(s=setTimeout(function(){T.abort("timeout")},c.timeout));try{v=1,n.send(m,k)}catch(C){if(!(2>v))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,t,o,a){var l,m,y,b,w,C=t;2!==v&&(v=2,s&&clearTimeout(s),n=undefined,i=a||"",T.readyState=e>0?4:0,l=e>=200&&300>e||304===e,o&&(b=pn(c,T,o)),b=fn(c,b,T,l),l?(c.ifModified&&(w=T.getResponseHeader("Last-Modified"),w&&(x.lastModified[r]=w),w=T.getResponseHeader("etag"),w&&(x.etag[r]=w)),204===e||"HEAD"===c.type?C="nocontent":304===e?C="notmodified":(C=b.state,m=b.data,y=b.error,l=!y)):(y=C,(e||!C)&&(C="error",0>e&&(e=0))),T.status=e,T.statusText=(t||C)+"",l?h.resolveWith(p,[m,C,T]):h.rejectWith(p,[T,C,y]),T.statusCode(g),g=undefined,u&&f.trigger(l?"ajaxSuccess":"ajaxError",[T,c,l?m:y]),d.fireWith(p,[T,C]),u&&(f.trigger("ajaxComplete",[T,c]),--x.active||x.event.trigger("ajaxStop")))}return T},getJSON:function(e,t,n){return x.get(e,t,n,"json")},getScript:function(e,t){return x.get(e,undefined,t,"script")}}),x.each(["get","post"],function(e,t){x[t]=function(e,n,r,i){return x.isFunction(n)&&(i=i||r,r=n,n=undefined),x.ajax({url:e,type:t,dataType:i,data:n,success:r})}});function pn(e,t,n){var r,i,o,s,a=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),r===undefined&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in a)if(a[i]&&a[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}s||(s=i)}o=o||s}return o?(o!==u[0]&&u.unshift(o),n[o]):undefined}function fn(e,t,n,r){var i,o,s,a,u,l={},c=e.dataTypes.slice();if(c[1])for(s in e.converters)l[s.toLowerCase()]=e.converters[s];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(s=l[u+" "+o]||l["* "+o],!s)for(i in l)if(a=i.split(" "),a[1]===o&&(s=l[u+" "+a[0]]||l["* "+a[0]])){s===!0?s=l[i]:l[i]!==!0&&(o=a[0],c.unshift(a[1]));break}if(s!==!0)if(s&&e["throws"])t=s(t);else try{t=s(t)}catch(p){return{state:"parsererror",error:s?p:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}x.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return x.globalEval(e),e}}}),x.ajaxPrefilter("script",function(e){e.cache===undefined&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),x.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=x("
+
+
+
+
+
+
+
+
+ {% block content %}{% endblock %}
+
+
+
+ {% with messages = get_flashed_messages() %}
+ {% if messages %}
+
+
+
+
+ {% for message in messages %}
+
+
+ Error:
+ {{ message }}
+
+ {% endfor %}
+
+
+
+
+
+ {% endif %}
+ {% endwith %}
+
+
+
+
diff --git a/casestudies-src/artificial_idp/templates/login.html b/casestudies-src/artificial_idp/templates/login.html
new file mode 100644
index 0000000..d3c5274
--- /dev/null
+++ b/casestudies-src/artificial_idp/templates/login.html
@@ -0,0 +1,28 @@
+{% extends "layout.html" %}
+{% block title %}Login{% endblock %}
+{% block content %}
+
+
Login
+
+
+{% endblock %}
diff --git a/casestudies-src/artificial_rp/Dockerfile b/casestudies-src/artificial_rp/Dockerfile
new file mode 100644
index 0000000..a516a21
--- /dev/null
+++ b/casestudies-src/artificial_rp/Dockerfile
@@ -0,0 +1,10 @@
+FROM composer as deps
+ADD ./composer.json ./composer.lock /app/
+RUN composer install --ignore-platform-reqs --no-scripts
+
+
+FROM php:7-fpm
+RUN pecl install mongodb
+RUN echo "extension=mongodb.so" >> /usr/local/etc/php/conf.d/mongodb.ini
+ADD ./ /var/www/html/
+COPY --from=deps /app/vendor/ /var/www/html/vendor/
diff --git a/casestudies-src/artificial_rp/README.md b/casestudies-src/artificial_rp/README.md
new file mode 100644
index 0000000..463d621
--- /dev/null
+++ b/casestudies-src/artificial_rp/README.md
@@ -0,0 +1,14 @@
+# integrator.com - Artificial RP
+
+## Extensions
+
+The applicationimplements a plugin system that enables a developer to easily integrate
+new IdPs and introduce vulnerabilities in the integrations.
+
+In particular, this version of the website implements (see [extensions](./extensions)):
+- AS SSO (`auth_server_sso`)
+- Google SSO (`google_sso`)
+- Facebook SSO (`facebook_sso`)
+- VK SSO (`vk_sso`)
+- Vulnerability in the check of the `state` parameter for every integration (`auth_server_state_check_vuln`, `fb_state_check_vuln`, `google_state_check_vuln`, `vk_state_check_vuln`)
+- Service Worker monitor for every integration (currently configured for AS) (`sw_monitor`)
diff --git a/casestudies-src/artificial_rp/composer.json b/casestudies-src/artificial_rp/composer.json
new file mode 100644
index 0000000..a355f59
--- /dev/null
+++ b/casestudies-src/artificial_rp/composer.json
@@ -0,0 +1,10 @@
+{
+ "require": {
+ "mongodb/mongodb": "^1.4",
+ "facebook/graph-sdk": "^5.7",
+ "klein/klein": "^2.1",
+ "guzzlehttp/guzzle": ">=5.0 <6.0",
+ "vkcom/vk-php-sdk": "^5.100",
+ "google/apiclient": "^2.0"
+ }
+}
diff --git a/casestudies-src/artificial_rp/composer.lock b/casestudies-src/artificial_rp/composer.lock
new file mode 100644
index 0000000..9de61f5
--- /dev/null
+++ b/casestudies-src/artificial_rp/composer.lock
@@ -0,0 +1,1083 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "a8a5264bb168b07372f14ad6cbaf9759",
+ "packages": [
+ {
+ "name": "facebook/graph-sdk",
+ "version": "5.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/facebook/php-graph-sdk.git",
+ "reference": "2d8250638b33d73e7a87add65f47fabf91f8ad9b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/facebook/php-graph-sdk/zipball/2d8250638b33d73e7a87add65f47fabf91f8ad9b",
+ "reference": "2d8250638b33d73e7a87add65f47fabf91f8ad9b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.4|^7.0"
+ },
+ "require-dev": {
+ "guzzlehttp/guzzle": "~5.0",
+ "mockery/mockery": "~0.8",
+ "phpunit/phpunit": "~4.0"
+ },
+ "suggest": {
+ "guzzlehttp/guzzle": "Allows for implementation of the Guzzle HTTP client",
+ "paragonie/random_compat": "Provides a better CSPRNG option in PHP 5"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Facebook\\": "src/Facebook/"
+ },
+ "files": [
+ "src/Facebook/polyfills.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Facebook Platform"
+ ],
+ "authors": [
+ {
+ "name": "Facebook",
+ "homepage": "https://github.com/facebook/php-graph-sdk/contributors"
+ }
+ ],
+ "description": "Facebook SDK for PHP",
+ "homepage": "https://github.com/facebook/php-graph-sdk",
+ "keywords": [
+ "facebook",
+ "sdk"
+ ],
+ "time": "2018-12-11T22:56:31+00:00"
+ },
+ {
+ "name": "firebase/php-jwt",
+ "version": "v5.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/firebase/php-jwt.git",
+ "reference": "feb0e820b8436873675fd3aca04f3728eb2185cb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/firebase/php-jwt/zipball/feb0e820b8436873675fd3aca04f3728eb2185cb",
+ "reference": "feb0e820b8436873675fd3aca04f3728eb2185cb",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=4.8 <=9"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Firebase\\JWT\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Neuman Vong",
+ "email": "neuman+pear@twilio.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Anant Narayanan",
+ "email": "anant@php.net",
+ "role": "Developer"
+ }
+ ],
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
+ "homepage": "https://github.com/firebase/php-jwt",
+ "keywords": [
+ "jwt",
+ "php"
+ ],
+ "time": "2020-03-25T18:49:23+00:00"
+ },
+ {
+ "name": "google/apiclient",
+ "version": "v2.4.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/google-api-php-client.git",
+ "reference": "1fdfe942f9aaf3064e621834a5e3047fccb3a6da"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/google-api-php-client/zipball/1fdfe942f9aaf3064e621834a5e3047fccb3a6da",
+ "reference": "1fdfe942f9aaf3064e621834a5e3047fccb3a6da",
+ "shasum": ""
+ },
+ "require": {
+ "firebase/php-jwt": "~2.0||~3.0||~4.0||~5.0",
+ "google/apiclient-services": "~0.13",
+ "google/auth": "^1.0",
+ "guzzlehttp/guzzle": "~5.3.1||~6.0",
+ "guzzlehttp/psr7": "^1.2",
+ "monolog/monolog": "^1.17|^2.0",
+ "php": ">=5.4",
+ "phpseclib/phpseclib": "~0.3.10||~2.0"
+ },
+ "require-dev": {
+ "cache/filesystem-adapter": "^0.3.2",
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0",
+ "phpcompatibility/php-compatibility": "^9.2",
+ "phpunit/phpunit": "^4.8|^5.0",
+ "squizlabs/php_codesniffer": "~2.3",
+ "symfony/css-selector": "~2.1",
+ "symfony/dom-crawler": "~2.1"
+ },
+ "suggest": {
+ "cache/filesystem-adapter": "For caching certs and tokens (using Google_Client::setCache)"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Google_": "src/"
+ },
+ "classmap": [
+ "src/Google/Service/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "Client library for Google APIs",
+ "homepage": "http://developers.google.com/api-client-library/php",
+ "keywords": [
+ "google"
+ ],
+ "time": "2020-03-26T15:30:32+00:00"
+ },
+ {
+ "name": "google/apiclient-services",
+ "version": "v0.132",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/google-api-php-client-services.git",
+ "reference": "317c8555eaea64a38594d9e617b59547cea45f3e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/google-api-php-client-services/zipball/317c8555eaea64a38594d9e617b59547cea45f3e",
+ "reference": "317c8555eaea64a38594d9e617b59547cea45f3e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8|^5"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "Google_Service_": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "Client library for Google APIs",
+ "homepage": "http://developers.google.com/api-client-library/php",
+ "keywords": [
+ "google"
+ ],
+ "time": "2020-04-22T00:25:30+00:00"
+ },
+ {
+ "name": "google/auth",
+ "version": "v1.8.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/googleapis/google-auth-library-php.git",
+ "reference": "c7b295feb248f138f462a1e6b7d635e4244204c5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/googleapis/google-auth-library-php/zipball/c7b295feb248f138f462a1e6b7d635e4244204c5",
+ "reference": "c7b295feb248f138f462a1e6b7d635e4244204c5",
+ "shasum": ""
+ },
+ "require": {
+ "firebase/php-jwt": "~2.0|~3.0|~4.0|~5.0",
+ "guzzlehttp/guzzle": "~5.3.1|~6.0",
+ "guzzlehttp/psr7": "^1.2",
+ "php": ">=5.4",
+ "psr/cache": "^1.0",
+ "psr/http-message": "^1.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^1.11",
+ "guzzlehttp/promises": "0.1.1|^1.3",
+ "kelvinmo/simplejwt": "^0.2.5",
+ "phpseclib/phpseclib": "^2",
+ "phpunit/phpunit": "^4.8.36|^5.7",
+ "sebastian/comparator": ">=1.2.3"
+ },
+ "suggest": {
+ "phpseclib/phpseclib": "May be used in place of OpenSSL for signing strings or for token management. Please require version ^2."
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Google\\Auth\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "description": "Google Auth Library for PHP",
+ "homepage": "http://github.com/google/google-auth-library-php",
+ "keywords": [
+ "Authentication",
+ "google",
+ "oauth2"
+ ],
+ "time": "2020-03-26T19:47:36+00:00"
+ },
+ {
+ "name": "guzzlehttp/guzzle",
+ "version": "5.3.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle.git",
+ "reference": "b87eda7a7162f95574032da17e9323c9899cb6b2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b87eda7a7162f95574032da17e9323c9899cb6b2",
+ "reference": "b87eda7a7162f95574032da17e9323c9899cb6b2",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/ringphp": "^1.1",
+ "php": ">=5.4.0",
+ "react/promise": "^2.2"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "phpunit/phpunit": "^4.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "rest",
+ "web service"
+ ],
+ "time": "2019-10-30T09:32:00+00:00"
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "1.6.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "239400de7a173fe9901b9ac7c06497751f00727a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
+ "reference": "239400de7a173fe9901b9ac7c06497751f00727a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0",
+ "psr/http-message": "~1.0",
+ "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
+ },
+ "provide": {
+ "psr/http-message-implementation": "1.0"
+ },
+ "require-dev": {
+ "ext-zlib": "*",
+ "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
+ },
+ "suggest": {
+ "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Psr7\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Tobias Schultze",
+ "homepage": "https://github.com/Tobion"
+ }
+ ],
+ "description": "PSR-7 message implementation that also provides common utility methods",
+ "keywords": [
+ "http",
+ "message",
+ "psr-7",
+ "request",
+ "response",
+ "stream",
+ "uri",
+ "url"
+ ],
+ "time": "2019-07-01T23:21:34+00:00"
+ },
+ {
+ "name": "guzzlehttp/ringphp",
+ "version": "1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/RingPHP.git",
+ "reference": "5e2a174052995663dd68e6b5ad838afd47dd615b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/5e2a174052995663dd68e6b5ad838afd47dd615b",
+ "reference": "5e2a174052995663dd68e6b5ad838afd47dd615b",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/streams": "~3.0",
+ "php": ">=5.4.0",
+ "react/promise": "~2.0"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "phpunit/phpunit": "~4.0"
+ },
+ "suggest": {
+ "ext-curl": "Guzzle will use specific adapters if cURL is present"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Ring\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.",
+ "abandoned": true,
+ "time": "2018-07-31T13:22:33+00:00"
+ },
+ {
+ "name": "guzzlehttp/streams",
+ "version": "3.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/streams.git",
+ "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5",
+ "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.0-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Stream\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Provides a simple abstraction over streams of data",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "Guzzle",
+ "stream"
+ ],
+ "abandoned": true,
+ "time": "2014-10-12T19:18:40+00:00"
+ },
+ {
+ "name": "klein/klein",
+ "version": "v2.1.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/klein/klein.php.git",
+ "reference": "6549676cc831b9417332b3d9485c64bbf7bac728"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/klein/klein.php/zipball/6549676cc831b9417332b3d9485c64bbf7bac728",
+ "reference": "6549676cc831b9417332b3d9485c64bbf7bac728",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/php-code-coverage": "^2.2",
+ "phpunit/phpunit": "^4.8",
+ "squizlabs/php_codesniffer": "1.4.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Klein\\": "src/Klein/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris O'Hara",
+ "email": "cohara87@gmail.com",
+ "homepage": "http://chris6f.com/",
+ "role": "Creator/Developer"
+ },
+ {
+ "name": "Trevor Suarez",
+ "email": "rican7@gmail.com",
+ "homepage": "https://trevorsuarez.com/",
+ "role": "Contributor/Developer"
+ }
+ ],
+ "description": "A lightning fast router for PHP",
+ "homepage": "https://github.com/klein/klein.php",
+ "keywords": [
+ "boilerplate",
+ "router",
+ "routing",
+ "sinatra"
+ ],
+ "time": "2017-02-01T23:08:58+00:00"
+ },
+ {
+ "name": "mongodb/mongodb",
+ "version": "1.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mongodb/mongo-php-library.git",
+ "reference": "dc43ba25fb593d6a2988e6a535b6f5386eda5b15"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/dc43ba25fb593d6a2988e6a535b6f5386eda5b15",
+ "reference": "dc43ba25fb593d6a2988e6a535b6f5386eda5b15",
+ "shasum": ""
+ },
+ "require": {
+ "ext-hash": "*",
+ "ext-json": "*",
+ "ext-mongodb": "^1.7",
+ "php": "^5.6 || ^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^5.7.27 || ^6.4 || ^8.3",
+ "sebastian/comparator": "^1.0 || ^2.0 || ^3.0",
+ "squizlabs/php_codesniffer": "^3.4",
+ "symfony/phpunit-bridge": "^4.4@dev"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "MongoDB\\": "src/"
+ },
+ "files": [
+ "src/functions.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "Apache-2.0"
+ ],
+ "authors": [
+ {
+ "name": "Andreas Braun",
+ "email": "andreas.braun@mongodb.com"
+ },
+ {
+ "name": "Jeremy Mikola",
+ "email": "jmikola@gmail.com"
+ },
+ {
+ "name": "Katherine Walker",
+ "email": "katherine.walker@mongodb.com"
+ }
+ ],
+ "description": "MongoDB driver library",
+ "homepage": "https://jira.mongodb.org/browse/PHPLIB",
+ "keywords": [
+ "database",
+ "driver",
+ "mongodb",
+ "persistence"
+ ],
+ "time": "2020-02-04T18:16:35+00:00"
+ },
+ {
+ "name": "monolog/monolog",
+ "version": "2.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/Seldaek/monolog.git",
+ "reference": "c861fcba2ca29404dc9e617eedd9eff4616986b8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c861fcba2ca29404dc9e617eedd9eff4616986b8",
+ "reference": "c861fcba2ca29404dc9e617eedd9eff4616986b8",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2",
+ "psr/log": "^1.0.1"
+ },
+ "provide": {
+ "psr/log-implementation": "1.0.0"
+ },
+ "require-dev": {
+ "aws/aws-sdk-php": "^2.4.9 || ^3.0",
+ "doctrine/couchdb": "~1.0@dev",
+ "elasticsearch/elasticsearch": "^6.0",
+ "graylog2/gelf-php": "^1.4.2",
+ "jakub-onderka/php-parallel-lint": "^0.9",
+ "php-amqplib/php-amqplib": "~2.4",
+ "php-console/php-console": "^3.1.3",
+ "phpspec/prophecy": "^1.6.1",
+ "phpunit/phpunit": "^8.3",
+ "predis/predis": "^1.1",
+ "rollbar/rollbar": "^1.3",
+ "ruflin/elastica": ">=0.90 <3.0",
+ "swiftmailer/swiftmailer": "^5.3|^6.0"
+ },
+ "suggest": {
+ "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB",
+ "doctrine/couchdb": "Allow sending log messages to a CouchDB server",
+ "elasticsearch/elasticsearch": "Allow sending log messages to an Elasticsearch server via official client",
+ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)",
+ "ext-mbstring": "Allow to work properly with unicode symbols",
+ "ext-mongodb": "Allow sending log messages to a MongoDB server (via driver)",
+ "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server",
+ "mongodb/mongodb": "Allow sending log messages to a MongoDB server (via library)",
+ "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib",
+ "php-console/php-console": "Allow sending log messages to Google Chrome",
+ "rollbar/rollbar": "Allow sending log messages to Rollbar",
+ "ruflin/elastica": "Allow sending log messages to an Elastic Search server"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Monolog\\": "src/Monolog"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jordi Boggiano",
+ "email": "j.boggiano@seld.be",
+ "homepage": "http://seld.be"
+ }
+ ],
+ "description": "Sends your logs to files, sockets, inboxes, databases and various web services",
+ "homepage": "http://github.com/Seldaek/monolog",
+ "keywords": [
+ "log",
+ "logging",
+ "psr-3"
+ ],
+ "time": "2019-12-20T14:22:59+00:00"
+ },
+ {
+ "name": "phpseclib/phpseclib",
+ "version": "2.0.27",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpseclib/phpseclib.git",
+ "reference": "34620af4df7d1988d8f0d7e91f6c8a3bf931d8dc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/34620af4df7d1988d8f0d7e91f6c8a3bf931d8dc",
+ "reference": "34620af4df7d1988d8f0d7e91f6c8a3bf931d8dc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "require-dev": {
+ "phing/phing": "~2.7",
+ "phpunit/phpunit": "^4.8.35|^5.7|^6.0",
+ "sami/sami": "~2.0",
+ "squizlabs/php_codesniffer": "~2.0"
+ },
+ "suggest": {
+ "ext-gmp": "Install the GMP (GNU Multiple Precision) extension in order to speed up arbitrary precision integer arithmetic operations.",
+ "ext-libsodium": "SSH2/SFTP can make use of some algorithms provided by the libsodium-php extension.",
+ "ext-mcrypt": "Install the Mcrypt extension in order to speed up a few other cryptographic operations.",
+ "ext-openssl": "Install the OpenSSL extension in order to speed up a wide variety of cryptographic operations."
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "phpseclib/bootstrap.php"
+ ],
+ "psr-4": {
+ "phpseclib\\": "phpseclib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jim Wigginton",
+ "email": "terrafrost@php.net",
+ "role": "Lead Developer"
+ },
+ {
+ "name": "Patrick Monnerat",
+ "email": "pm@datasphere.ch",
+ "role": "Developer"
+ },
+ {
+ "name": "Andreas Fischer",
+ "email": "bantu@phpbb.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Hans-Jürgen Petrich",
+ "email": "petrich@tronic-media.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Graham Campbell",
+ "email": "graham@alt-three.com",
+ "role": "Developer"
+ }
+ ],
+ "description": "PHP Secure Communications Library - Pure-PHP implementations of RSA, AES, SSH2, SFTP, X.509 etc.",
+ "homepage": "http://phpseclib.sourceforge.net",
+ "keywords": [
+ "BigInteger",
+ "aes",
+ "asn.1",
+ "asn1",
+ "blowfish",
+ "crypto",
+ "cryptography",
+ "encryption",
+ "rsa",
+ "security",
+ "sftp",
+ "signature",
+ "signing",
+ "ssh",
+ "twofish",
+ "x.509",
+ "x509"
+ ],
+ "funding": [
+ {
+ "url": "https://github.com/terrafrost",
+ "type": "github"
+ },
+ {
+ "url": "https://www.patreon.com/phpseclib",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpseclib/phpseclib",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2020-04-04T23:17:33+00:00"
+ },
+ {
+ "name": "psr/cache",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/cache.git",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/cache/zipball/d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "reference": "d11b50ad223250cf17b86e38383413f5a6764bf8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Cache\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for caching libraries",
+ "keywords": [
+ "cache",
+ "psr",
+ "psr-6"
+ ],
+ "time": "2016-08-06T20:24:11+00:00"
+ },
+ {
+ "name": "psr/http-message",
+ "version": "1.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/http-message.git",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Http\\Message\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for HTTP messages",
+ "homepage": "https://github.com/php-fig/http-message",
+ "keywords": [
+ "http",
+ "http-message",
+ "psr",
+ "psr-7",
+ "request",
+ "response"
+ ],
+ "time": "2016-08-06T14:39:51+00:00"
+ },
+ {
+ "name": "psr/log",
+ "version": "1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-fig/log.git",
+ "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
+ "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Psr\\Log\\": "Psr/Log/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "PHP-FIG",
+ "homepage": "http://www.php-fig.org/"
+ }
+ ],
+ "description": "Common interface for logging libraries",
+ "homepage": "https://github.com/php-fig/log",
+ "keywords": [
+ "log",
+ "psr",
+ "psr-3"
+ ],
+ "time": "2020-03-23T09:12:05+00:00"
+ },
+ {
+ "name": "ralouphie/getallheaders",
+ "version": "3.0.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/ralouphie/getallheaders.git",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+ "reference": "120b605dfeb996808c31b6477290a714d356e822",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.6"
+ },
+ "require-dev": {
+ "php-coveralls/php-coveralls": "^2.1",
+ "phpunit/phpunit": "^5 || ^6.5"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/getallheaders.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ralph Khattar",
+ "email": "ralph.khattar@gmail.com"
+ }
+ ],
+ "description": "A polyfill for getallheaders.",
+ "time": "2019-03-08T08:55:37+00:00"
+ },
+ {
+ "name": "react/promise",
+ "version": "v2.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/reactphp/promise.git",
+ "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/reactphp/promise/zipball/31ffa96f8d2ed0341a57848cbb84d88b89dd664d",
+ "reference": "31ffa96f8d2ed0341a57848cbb84d88b89dd664d",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.8"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "React\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jan Sorgalla",
+ "email": "jsorgalla@gmail.com"
+ }
+ ],
+ "description": "A lightweight implementation of CommonJS Promises/A for PHP",
+ "keywords": [
+ "promise",
+ "promises"
+ ],
+ "time": "2019-01-07T21:25:54+00:00"
+ },
+ {
+ "name": "vkcom/vk-php-sdk",
+ "version": "5.101.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/VKCOM/vk-php-sdk.git",
+ "reference": "46a8f76503050c0c6730359fc234a4d6448b8653"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/VKCOM/vk-php-sdk/zipball/46a8f76503050c0c6730359fc234a4d6448b8653",
+ "reference": "46a8f76503050c0c6730359fc234a4d6448b8653",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^6",
+ "vkcom/vk-api-schema": "^5.80.1"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "VK\\": "src/VK"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "VK PHP SDK",
+ "homepage": "https://github.com/VKCOM/vk-php-sdk",
+ "keywords": [
+ "sdk",
+ "vk"
+ ],
+ "time": "2019-08-16T12:13:20+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": [],
+ "plugin-api-version": "1.1.0"
+}
diff --git a/casestudies-src/artificial_rp/extensions/auth_server_sso/autoload.php b/casestudies-src/artificial_rp/extensions/auth_server_sso/autoload.php
new file mode 100644
index 0000000..0fc4c25
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/auth_server_sso/autoload.php
@@ -0,0 +1,19 @@
+registerExtension([
+ 'name' => 'auth_server_sso',
+ 'tags' => ['as-login'],
+ 'depends_on' => [],
+], function () {
+ // register or modify components
+ ComponentsManager::instance()->registerComponent(
+ 'routes', 'ascallback', new auth_server_sso\Callback());
+ ComponentsManager::instance()->registerComponent(
+ 'login-button', 'asbutton', new auth_server_sso\LoginButton());
+});
+
diff --git a/casestudies-src/artificial_rp/extensions/auth_server_sso/callback.php b/casestudies-src/artificial_rp/extensions/auth_server_sso/callback.php
new file mode 100644
index 0000000..c1a78c6
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/auth_server_sso/callback.php
@@ -0,0 +1,79 @@
+respond(array('GET','POST'), $this->callback_path, function ($req,$res,$service) {
+ return $this->callback($service);
+ });
+ }
+
+ public function callback($service){
+ global $config;
+ global $users;
+
+ if (!isset($_GET['code']) || !isset($_GET['state']))
+ die("Error: bad request.");
+
+ if ($_GET['state'] !== $_SESSION['as_state'])
+ die("Error: state matching error.");
+
+ $code = $_GET['code'];
+
+ $token_uri = sprintf(
+ "https://auth-server.com/oauth2/token?client_id=%s&redirect_uri=%s&client_secret=%s&code=%s",
+ $config['client_id'], urlencode($this->red_uri), $config['secret_key'], $code
+ );
+
+ $response = http($token_uri);
+ if (!isset($response->token))
+ die("Error getting access token!");
+
+ $email=http('https://auth-server.com/info', false, $response->token);
+
+
+ $document = array('username' => (string) $email->email);
+ if ($users->count($document) == 1)
+ login_with_access_token($email->email, $response->token, $email->email, "as");
+ else {
+ register_with_access_token($email->email, $response->token, $email->email, "as");
+ login_with_access_token($email->email, $response->token, $email->email, "as");
+ }
+
+
+
+ $_SESSION['as_access_token'] = $response->token;
+ $service->render(__DIR__.'/template_success.php', ['access_token' => $response->token, 'email' => $email->email]);
+ }
+}
diff --git a/casestudies-src/artificial_rp/extensions/auth_server_sso/config.php b/casestudies-src/artificial_rp/extensions/auth_server_sso/config.php
new file mode 100644
index 0000000..f5aca55
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/auth_server_sso/config.php
@@ -0,0 +1,8 @@
+ '67538654696',
+ 'secret_key' => 'Pq9LAGblNUnLswc3dtlXlccSXOe31S6I'
+];
diff --git a/casestudies-src/artificial_rp/extensions/auth_server_sso/login_button.php b/casestudies-src/artificial_rp/extensions/auth_server_sso/login_button.php
new file mode 100644
index 0000000..9f406e9
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/auth_server_sso/login_button.php
@@ -0,0 +1,26 @@
+scopes, urlencode($this->red_uri), $state
+ );
+
+ return sprintf(
+ '
'.
+ ''.
+ 'Log in with AS!', $login_uri);
+ }
+}
diff --git a/casestudies-src/artificial_rp/extensions/auth_server_sso/template_success.php b/casestudies-src/artificial_rp/extensions/auth_server_sso/template_success.php
new file mode 100644
index 0000000..3a9d168
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/auth_server_sso/template_success.php
@@ -0,0 +1,11 @@
+
+
+
+
+
You have been successfully logged in:
+
Access Token: access_token; ?>
+
User: email; ?>
+
+
diff --git a/casestudies-src/artificial_rp/extensions/auth_server_state_check_vuln/autoload.php b/casestudies-src/artificial_rp/extensions/auth_server_state_check_vuln/autoload.php
new file mode 100644
index 0000000..ecfd3fe
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/auth_server_state_check_vuln/autoload.php
@@ -0,0 +1,24 @@
+registerExtension([
+ 'name' => 'auth_server_sso_state_check_vuln',
+ 'tags' => ['as-state-check-vuln'],
+ 'depends_on' => ['as-login']
+ ], function () {
+ ComponentsManager
+ ::instance()
+ ->registerComponent('routes', 'ascallback', new auth_server_sso\FlawedCallback());
+ });
+}
diff --git a/casestudies-src/artificial_rp/extensions/facebook_sso/autoload.php b/casestudies-src/artificial_rp/extensions/facebook_sso/autoload.php
new file mode 100644
index 0000000..ed55e57
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/facebook_sso/autoload.php
@@ -0,0 +1,20 @@
+registerExtension([
+ 'name' => 'facebook_sso',
+ 'tags' => ['fb-login', 'fb-association'],
+ 'depends_on' => [],
+], function () {
+ // register or modify components
+ ComponentsManager::instance()->registerComponent(
+ 'routes', 'fbcallback', new facebook_sso\FBCallback());
+ ComponentsManager::instance()->registerComponent(
+ 'login-button', 'fbbutton', new facebook_sso\FBLoginButton());
+ ComponentsManager::instance()->registerComponent(
+ 'settings-button', 'fbbutton', new facebook_sso\FBSettingsButton());
+});
diff --git a/casestudies-src/artificial_rp/extensions/facebook_sso/callback.php b/casestudies-src/artificial_rp/extensions/facebook_sso/callback.php
new file mode 100644
index 0000000..30468fb
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/facebook_sso/callback.php
@@ -0,0 +1,124 @@
+respond(array('GET','POST'), $this->callback_path, function ($req,$res, $service) {
+ return $this->fb_callback($service);
+ });
+ }
+
+ public function fb_callback($service) {
+ list($accessToken, $facebook_id, $email) = $this->getAccessToken();
+ //TODO: if email is already present in db log that user, otherwise create a new user and associate the FB account
+
+
+ $msg = $this->associateAccount($accessToken, $facebook_id, $email);
+ $service->render(__DIR__.'/template_success.php', ['access_token' => $accessToken, 'msg' => $msg]);
+ }
+
+ function getAccessToken() {
+ global $fb;
+ global $fb_config;
+ $helper = $fb->getRedirectLoginHelper();
+
+ try {
+ $accessToken = $helper->getAccessToken($this->red_uri);
+ } catch(\Facebook\Exceptions\FacebookResponseException $e) {
+ // When Graph returns an error
+ echo 'Graph returned an error: ' . $e->getMessage();
+ return;
+ } catch(\Facebook\Exceptions\FacebookSDKException $e) {
+ // When validation fails or other local issues
+ echo 'Facebook SDK returned an error: ' . $e->getMessage();
+ return;
+ }
+
+ if (! isset($accessToken)) {
+ if ($helper->getError()) {
+ header('HTTP/1.0 401 Unauthorized');
+ header('Content-type: text/plain');
+
+ echo "Error: " . $helper->getError() . "\n";
+ echo "Error Code: " . $helper->getErrorCode() . "\n";
+ echo "Error Reason: " . $helper->getErrorReason() . "\n";
+ echo "Error Description: " . $helper->getErrorDescription() . "\n";
+ } else {
+ header('HTTP/1.0 400 Bad Request');
+ echo 'Bad request';
+ }
+ return;
+ }
+
+
+ // Logged in
+ //echo '
Access Token
';
+ //var_dump($accessToken->getValue());
+
+ // The OAuth 2.0 client handler helps us manage access tokens
+ $oAuth2Client = $fb->getOAuth2Client();
+
+ // Get the access token metadata from /debug_token
+ $tokenMetadata = $oAuth2Client->debugToken($accessToken);
+ //echo '
Metadata
';
+ //var_dump($tokenMetadata);
+
+ // Validation (these will throw FacebookSDKException's when they fail)
+ $tokenMetadata->validateAppId($fb_config['app_id']);
+ // If you know the user ID this access token belongs to, you can validate it here
+ //$tokenMetadata->validateUserId('123');
+ $tokenMetadata->validateExpiration();
+
+ if (! $accessToken->isLongLived()) {
+ // Exchanges a short-lived access token for a long-lived one
+ try {
+ $accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
+ } catch (\Facebook\Exceptions\FacebookSDKException $e) {
+ echo "
Error getting long-lived access token: " . $e->getMessage() . "
\n\n";
+ return;
+ }
+
+ echo '
Long-lived
';
+ var_dump($accessToken->getValue());
+ }
+ $fb->setDefaultAccessToken($accessToken);
+
+ // Get the email
+ $response = $fb->get('/me?locale=en_US&fields=name,email');
+ $userNode = $response->getGraphUser();
+ $email = $userNode->getField('email');
+
+ // Get facebook user id
+ $facebook_id = $tokenMetadata->getProperty('user_id');
+
+ return array($accessToken, $facebook_id, $email);
+ }
+
+ function associateAccount($accessToken, $facebook_id, $email) {
+ global $users;
+
+ $document = array('username' => (string) $email);
+
+ // TODO: add management if register or login functions return false
+ if ($users->count($document) == 1){
+ login_with_access_token($email, $accessToken, $facebook_id, "fb");
+ $msg = 'You have been successfully logged in';
+ } else {
+ if (register_with_access_token($email, $accessToken, $facebook_id, "fb")) {
+ $msg = 'You have been successfully registered';
+ } else {
+ echo "error registering user";
+ }
+ }
+ // $_SESSION['fb_access_token'] = (string) $accessToken;
+
+ return $msg;
+ }
+}
diff --git a/casestudies-src/artificial_rp/extensions/facebook_sso/config.php b/casestudies-src/artificial_rp/extensions/facebook_sso/config.php
new file mode 100644
index 0000000..739d6ce
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/facebook_sso/config.php
@@ -0,0 +1,26 @@
+ '390639234906615',
+ 'app_secret' => 'b0b2f4c7196624ec617cdd022c044de5',
+ 'default_graph_version' => 'v3.2',
+];
+//Set custom proxied HTTP Client
+if (getenv('HTTP_PROXY') && getenv('HTTP_PROXY_PORT')) {
+ $proxied_client = new \GuzzleHttp\Client([
+ 'defaults' => [
+ 'config' => [
+ 'curl' => [
+ CURLOPT_SSL_VERIFYHOST => 0,
+ CURLOPT_SSL_VERIFYPEER => 0,
+ CURLOPT_PROXY => getenv('HTTP_PROXY'),
+ CURLOPT_PROXYPORT => getenv('HTTP_PROXY_PORT'),
+ ]
+ ]
+ ]
+ ]);
+ $fb_config['http_client_handler'] = $proxied_client;
+}
+$fb = new \Facebook\Facebook($fb_config);
diff --git a/casestudies-src/artificial_rp/extensions/facebook_sso/login_button.php b/casestudies-src/artificial_rp/extensions/facebook_sso/login_button.php
new file mode 100644
index 0000000..b96e370
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/facebook_sso/login_button.php
@@ -0,0 +1,24 @@
+getRedirectLoginHelper();
+ $this->login_uri = $helper->getLoginUrl($this->red_uri, $this->permissions);
+ }
+
+ public function render() {
+ return sprintf(
+ '
'.
+ ''.
+ 'Log in with Facebook!', $this->login_uri);
+ }
+}
diff --git a/casestudies-src/artificial_rp/extensions/facebook_sso/settings_button.php b/casestudies-src/artificial_rp/extensions/facebook_sso/settings_button.php
new file mode 100644
index 0000000..f381cdb
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/facebook_sso/settings_button.php
@@ -0,0 +1,163 @@
+respond(array('GET','POST'), '/connect/facebook', function ($req, $res, $service) {
+ if(!isset($req->code)) {
+ global $fb;
+ $helper = $fb->getRedirectLoginHelper();
+ $this->assoc_uri = $helper->getLoginUrl($this->red_uri, $this->permissions);
+ header("Location: $this->assoc_uri");
+ } else {
+ list($accessToken, $facebook_id) = $this->getAccessToken();
+ $this->connect($accessToken, $facebook_id);
+ header("Location: /settings");
+ }
+ });
+ $klein->respond(array('GET','POST'), '/disconnect/facebook', function ($req, $res, $service) {
+ $this->disconnect();
+ header("Location: /settings");
+ });
+ }
+
+ public function render() {
+ if ($_SESSION['fb_loggedin'] === true) {
+ return sprintf(
+ '
'.
+ ''.
+ 'Disassociate Facebook account!', $this->disassoc_uri);
+ } else {
+ return sprintf(
+ '
'.
+ ''.
+ 'Associate Facebook account!', $this->assoc_uri);
+ }
+ }
+
+ function getAccessToken() {
+ global $fb;
+ global $fb_config;
+ $helper = $fb->getRedirectLoginHelper();
+
+ try {
+ $accessToken = $helper->getAccessToken($this->red_uri);
+ } catch(\Facebook\Exceptions\FacebookResponseException $e) {
+ // When Graph returns an error
+ echo 'Graph returned an error: ' . $e->getMessage();
+ return;
+ } catch(\Facebook\Exceptions\FacebookSDKException $e) {
+ // When validation fails or other local issues
+ echo 'Facebook SDK returned an error: ' . $e->getMessage();
+ return;
+ }
+
+ if (! isset($accessToken)) {
+ if ($helper->getError()) {
+ header('HTTP/1.0 401 Unauthorized');
+ header('Content-type: text/plain');
+
+ echo "Error: " . $helper->getError() . "\n";
+ echo "Error Code: " . $helper->getErrorCode() . "\n";
+ echo "Error Reason: " . $helper->getErrorReason() . "\n";
+ echo "Error Description: " . $helper->getErrorDescription() . "\n";
+ } else {
+ header('HTTP/1.0 400 Bad Request');
+ echo 'Bad request';
+ }
+ return;
+ }
+
+
+ // Logged in
+ //echo '
Access Token
';
+ //var_dump($accessToken->getValue());
+
+ // The OAuth 2.0 client handler helps us manage access tokens
+ $oAuth2Client = $fb->getOAuth2Client();
+
+ // Get the access token metadata from /debug_token
+ $tokenMetadata = $oAuth2Client->debugToken($accessToken);
+ //echo '
Metadata
';
+ //var_dump($tokenMetadata);
+
+ // Validation (these will throw FacebookSDKException's when they fail)
+ $tokenMetadata->validateAppId($fb_config['app_id']);
+ // If you know the user ID this access token belongs to, you can validate it here
+ //$tokenMetadata->validateUserId('123');
+ $tokenMetadata->validateExpiration();
+
+ if (! $accessToken->isLongLived()) {
+ // Exchanges a short-lived access token for a long-lived one
+ try {
+ $accessToken = $oAuth2Client->getLongLivedAccessToken($accessToken);
+ } catch (\Facebook\Exceptions\FacebookSDKException $e) {
+ echo "
Error getting long-lived access token: " . $e->getMessage() . "
\n\n";
+ return;
+ }
+
+ echo '
Long-lived
';
+ var_dump($accessToken->getValue());
+ }
+ $fb->setDefaultAccessToken($accessToken);
+
+ // Get the email
+ $response = $fb->get('/me?locale=en_US&fields=name,email');
+ $userNode = $response->getGraphUser();
+ $email = $userNode->getField('email');
+
+ // Get facebook user id
+ $facebook_id = $tokenMetadata->getProperty('user_id');
+
+ return array($accessToken, $facebook_id);
+ }
+
+ public function disconnect() {
+ global $users;
+ global $fb;
+
+ $users->updateOne(
+ array("fb_user_id" => $_SESSION["fb_user_id"]),
+ array('$unset' => array('fb_user_id' => true, "fb_access_token" => true)),
+ );
+ // Comment these two lines if you want the IdP to ask permissions each time
+ $fb->setDefaultAccessToken($_SESSION["fb_access_token"]);
+ $response = $fb->delete("/".$_SESSION["fb_user_id"]."/permissions");
+
+ $_SESSION["fb_user_id"] = null;
+ $_SESSION["fb_access_token"] = null;
+ $_SESSION["fb_loggedin"] = false;
+ }
+
+ public function connect($accessToken, $idpId) {
+ global $users;
+
+ echo $_SESSION['username'];
+ $ret = $users->updateOne(
+ array("username" => $_SESSION['username']),
+ array('$set' => array('fb_user_id' => $idpId, "fb_access_token" => $accessToken)),
+ );
+
+ $document = array('fb_user_id' => (string) $idpId);
+ $cursor = $users->find($document);
+ $it = new \IteratorIterator($cursor);
+ $it->rewind();
+ while($user = $it->current()){
+ // User is logged in into IdP
+ $_SESSION["fb_loggedin"] = true;
+ $_SESSION["fb_user_id"] = $idpId;
+ $_SESSION["fb_access_token"] = $accessToken;
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/casestudies-src/artificial_rp/extensions/facebook_sso/template_success.php b/casestudies-src/artificial_rp/extensions/facebook_sso/template_success.php
new file mode 100644
index 0000000..b94620a
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/facebook_sso/template_success.php
@@ -0,0 +1,10 @@
+
+
+
+
+
msg; ?>:
+
Access Token: access_token; ?>
+
+
diff --git a/casestudies-src/artificial_rp/extensions/fb_state_check_vuln/autoload.php b/casestudies-src/artificial_rp/extensions/fb_state_check_vuln/autoload.php
new file mode 100644
index 0000000..bdb1361
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/fb_state_check_vuln/autoload.php
@@ -0,0 +1,25 @@
+registerExtension([
+ 'name' => 'facebook_sso_state_check_vuln',
+ 'tags' => ['fb-state-check-vuln'],
+ 'depends_on' => ['fb-login']
+ ], function () {
+ ComponentsManager
+ ::instance()
+ ->registerComponent('routes', 'fbcallback', new facebook_sso\FlawedFBCallback());
+ });
+}
diff --git a/casestudies-src/artificial_rp/extensions/google_sso/autoload.php b/casestudies-src/artificial_rp/extensions/google_sso/autoload.php
new file mode 100644
index 0000000..ad2b4a7
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/google_sso/autoload.php
@@ -0,0 +1,22 @@
+registerExtension([
+ 'name' => 'google_sso',
+ 'tags' => ['google-login'],
+ 'depends_on' => [],
+], function () {
+ // register or modify components
+ ComponentsManager::instance()->registerComponent(
+ 'routes', 'gcallback', new google_sso\GCallback());
+ ComponentsManager::instance()->registerComponent(
+ 'login-button', 'gbutton', new google_sso\GLoginButton());
+});
+
+
diff --git a/casestudies-src/artificial_rp/extensions/google_sso/callback.php b/casestudies-src/artificial_rp/extensions/google_sso/callback.php
new file mode 100644
index 0000000..de1bf89
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/google_sso/callback.php
@@ -0,0 +1,43 @@
+respond(array('GET','POST'), $this->callback_path, function ($req,$res,$service) {
+ return $this->callback($service);
+ });
+ }
+
+ public function callback($service){
+ global $google_config;
+ global $g_client;
+
+ if (!isset($_GET['code']) || !isset($_GET['state']))
+ die("Error: bad request.");
+
+ if ($_GET['state'] !== $_SESSION['google_state'])
+ die("Error: state matching error.");
+
+ $code = $_GET['code'];
+
+ $g_client->setRedirectUri($this->red_uri);
+ $g_client->authenticate($code);
+ $access_token = $g_client->getAccessToken();
+
+ if (!isset($access_token)){
+ var_dump($access_token);
+ var_dump($g_client);
+ die('Error: invalid response.');
+ }
+
+ $_SESSION['google_access_token'] = $access_token;
+ $service->render(__DIR__.'/template_success.php', ['access_token' => $access_token['access_token']]);
+ }
+}
diff --git a/casestudies-src/artificial_rp/extensions/google_sso/client_secret.json b/casestudies-src/artificial_rp/extensions/google_sso/client_secret.json
new file mode 100644
index 0000000..844ebe7
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/google_sso/client_secret.json
@@ -0,0 +1 @@
+{"web":{"client_id":"996805361802-2tbmasivof6joftmsfqtgcuehpuokl71.apps.googleusercontent.com","project_id":"nimble-mode-275814","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"C3LhKyF23gG3I1RDmuMdpmUJ"}}
diff --git a/casestudies-src/artificial_rp/extensions/google_sso/config.php b/casestudies-src/artificial_rp/extensions/google_sso/config.php
new file mode 100644
index 0000000..f7d0229
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/google_sso/config.php
@@ -0,0 +1,26 @@
+setAuthConfig(__DIR__.'/client_secret.json');
+
+if (getenv('HTTP_PROXY') && getenv('HTTP_PROXY_PORT')) {
+ $httpClient = new \GuzzleHttp\Client([
+ 'defaults' => [
+ 'config' => [
+ 'curl' => [
+ CURLOPT_SSL_VERIFYHOST => 0,
+ CURLOPT_SSL_VERIFYPEER => 0,
+ CURLOPT_PROXY => getenv('HTTP_PROXY'),
+ CURLOPT_PROXYPORT => getenv('HTTP_PROXY_PORT'),
+ ]
+ ]
+ ]
+ ]);
+ $g_client->setHttpClient($httpClient);
+}
diff --git a/casestudies-src/artificial_rp/extensions/google_sso/login_button.php b/casestudies-src/artificial_rp/extensions/google_sso/login_button.php
new file mode 100644
index 0000000..077e9f6
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/google_sso/login_button.php
@@ -0,0 +1,28 @@
+addScope(\Google_Service_Drive::DRIVE_METADATA_READONLY);
+ $g_client->setRedirectUri($this->red_uri);
+ $g_client->setAccessType('offline');
+ $g_client->setState($state);
+
+ $login_uri = $g_client->createAuthUrl();
+
+ return sprintf(
+ '
'.
+ ''.
+ 'Log in with Google!', $login_uri);
+ }
+}
diff --git a/casestudies-src/artificial_rp/extensions/google_sso/template_success.php b/casestudies-src/artificial_rp/extensions/google_sso/template_success.php
new file mode 100644
index 0000000..7b90618
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/google_sso/template_success.php
@@ -0,0 +1,10 @@
+
+
+
+
+
You have been successfully logged in:
+
Access Token: access_token; ?>
+
+
diff --git a/casestudies-src/artificial_rp/extensions/google_state_check_vuln/autoload.php b/casestudies-src/artificial_rp/extensions/google_state_check_vuln/autoload.php
new file mode 100644
index 0000000..7a4c1bc
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/google_state_check_vuln/autoload.php
@@ -0,0 +1,24 @@
+registerExtension([
+ 'name' => 'google_sso_state_check_vuln',
+ 'tags' => ['google-state-check-vuln'],
+ 'depends_on' => ['google-login']
+ ], function () {
+ ComponentsManager
+ ::instance()
+ ->registerComponent('routes', 'gcallback', new google_sso\FlawedGCallback());
+ });
+}
diff --git a/casestudies-src/artificial_rp/extensions/sw_monitor/autoload.php b/casestudies-src/artificial_rp/extensions/sw_monitor/autoload.php
new file mode 100644
index 0000000..c269b2e
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/sw_monitor/autoload.php
@@ -0,0 +1,31 @@
+respond(array('GET','POST'), '*', function ($req,$res,$service) {
+ if ($req->pathname() != "/sw.js")
+ echo "";
+ });
+ $klein->respond(array('GET','POST'), '/sw.js', function ($req,$res,$service) {
+ header("Content-Type: text/javascript");
+ echo file_get_contents(__DIR__.'/sw.js');
+ });
+ }
+ }
+}
+
+
+namespace {
+ ExtensionsManager::instance()->registerExtension([
+ 'name' => 'sso_sw_monitor',
+ 'tags' => ['sw'],
+ 'depends_on' => []
+ ], function () {
+ ComponentsManager
+ ::instance()
+ ->registerComponent('routes', 'monitor', new sw\SWInstall());
+ });
+}
+
diff --git a/casestudies-src/artificial_rp/extensions/sw_monitor/install.js b/casestudies-src/artificial_rp/extensions/sw_monitor/install.js
new file mode 100644
index 0000000..314e1ed
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/sw_monitor/install.js
@@ -0,0 +1,23 @@
+(function () {
+ if ('serviceWorker' in navigator) {
+ if (navigator.serviceWorker.controller) {
+ navigator.serviceWorker.controller.postMessage({
+ type: 'fetch',
+ url: window.location.href
+ });
+ } else {
+ navigator.serviceWorker.register(
+ '/sw.js', {'scope': '/'}
+ ).then(function(registration) {
+ // Registration was successful
+ console.log('ServiceWorker registration successful with scope: ', registration.scope);
+ // Note that it won't control this instance of this page,
+ // it only takes effect for pages in its scope
+ // loaded *after* it's installed.
+ window.location.reload();
+ }, function(err) {
+ console.log('ServiceWorker registration failed: ', err);
+ });
+ }
+ }
+})();
diff --git a/casestudies-src/artificial_rp/extensions/sw_monitor/sw.js b/casestudies-src/artificial_rp/extensions/sw_monitor/sw.js
new file mode 100644
index 0000000..48fc944
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/sw_monitor/sw.js
@@ -0,0 +1,92 @@
+self.importScripts("/extensions/sw_monitor/sw_include.js")
+self.importScripts("/extensions/sw_monitor/sw_config_auth_server.js")
+
+/* in(serviceWorkerFetch(b), (u:Uri, cs__1000, sw__ref:Uri, sw__p:Page, sw__aj:Ajax)) */
+self.addEventListener('fetch', function(event) {
+ try {
+ let u = new URL(event.request.url);
+ let cs__1000 = event.request;
+ let sw__ref = event.request.referer;
+ /* let =httpGet() = cs__1000 */
+ if (!(("GET") == ((cs__1000).method))) throw new MatchFail();
+ /* let uri(=https(), =h, =loginpath(), =nullParams()) = u */
+ if (("https") == ((u).protocol.slice(0,-1)) && (h) == ((u).host) && (loginpath) == ((u).pathname) && ("") == ((u).search)) {
+ event.respondWith((async () => {
+ try {
+ /* out(rawRequest(b), (u, cs__1000, sw__ref, sw__p, sw__aj)) */
+ var __fetchd = await fetch((u).href, {
+ method: (cs__1000).method,
+ headers: event.request.headers,
+ });
+ /* in(serviceWorkerResult(b), (=u, cs__1100:HttpResponse, cs__1101:ReferrerPolicy, sw__foo:XDR, sw__corr:bitstring)) */
+ let cs__1100 = __fetchd;
+ let cs__1101 = __fetchd.headers['referrer-policy'];
+ /* let httpOk(pagewithlink(uri(=https(), =fb, =oauthpath(), codereqparams(=appid, =uri(https(), h, callbackpath(), nullParams()), state)))) = cs__1100 */
+ if (!((cs__1100).status == 200)) throw new MatchFail();
+ var __fetchd_body = await (cs__1100).clone().text();
+ var __pagewithlink_match = (new RegExp((u).protocol.slice(0,-1)+"://"+fb+oauthpath+"\\?"+"(?=.*?=(?:[^&]+))[^\\\"\\' ]+")).exec(__fetchd_body);
+ if (!((__pagewithlink_match))) throw new MatchFail();
+ if (!(("https") == ((new URL((__pagewithlink_match)[0])).protocol.slice(0,-1)))) throw new MatchFail();
+ if (!((fb) == ((new URL((__pagewithlink_match)[0])).host))) throw new MatchFail();
+ if (!((oauthpath) == ((new URL((__pagewithlink_match)[0])).pathname))) throw new MatchFail();
+ if (!((appid) == (codereqparams((new URL((__pagewithlink_match)[0])).search)[0]))) throw new MatchFail();
+ if (!(("https") == ((codereqparams((new URL((__pagewithlink_match)[0])).search)[1]).protocol.slice(0,-1)))) throw new MatchFail();
+ if (!((h) == ((codereqparams((new URL((__pagewithlink_match)[0])).search)[1]).host))) throw new MatchFail();
+ if (!((callbackpath) == ((codereqparams((new URL((__pagewithlink_match)[0])).search)[1]).pathname))) throw new MatchFail();
+ if (!(("") == ((codereqparams((new URL((__pagewithlink_match)[0])).search)[1]).search))) throw new MatchFail();
+ let state = codereqparams((new URL((__pagewithlink_match)[0])).search)[2];
+ /* let =unsafeUrl() = cs__1101 */
+ /* insert MRPSessions(state) */
+ await MRPSessions.insert({col_1: state})
+ /* out(serviceWorkerSendHttpResponse(b), (u, cs__1100, cs__1101, sw__foo, sw__corr)) */
+ var __response = (cs__1100)
+ if (cs__1101)
+ __response = await appendHeader(__response, 'referrer-policy', cs__1101)
+ return cleanResponse(__response);
+ } catch (e) {
+ console.log(e)
+ console.log('Match error: '+event.request.url)
+ return errorResponse('match error!');
+ }
+ })());
+ } else {
+ /* let uri(=https(), =h, =callbackpath(), coderesparams(code, state)) = u */
+ if (("https") == ((u).protocol.slice(0,-1)) && (h) == ((u).host) && (callbackpath) == ((u).pathname)) {
+ let code = coderesparams((u).search)[0];
+ let state = coderesparams((u).search)[1];
+ event.respondWith((async () => {
+ try {
+ /* get MRPSessions(=state) in */
+ var __table_res = await MRPSessions.findOne({col_1: {$eq: state}})
+ if (!(__table_res)) throw new MatchFail();
+ if (!((state) == (__table_res.col_1))) throw new MatchFail();
+ /* out(rawRequest(b), (u, cs__1000, sw__ref, sw__p, sw__aj)) */
+ var __fetchd = await fetch((u).href, {
+ method: (cs__1000).method,
+ headers: event.request.headers,
+ });
+ /* in(serviceWorkerResult(b), (=u, cs__1200:HttpResponse, cs__1201:ReferrerPolicy, sw__foo:XDR, sw__corr:bitstring)) */
+ let cs__1200 = __fetchd;
+ let cs__1201 = __fetchd.headers['referrer-policy'];
+ /* let =httpOk(success()) = cs__1200 */
+ if (!((cs__1200).status == 200)) throw new MatchFail();
+ var __fetchd_body = await (cs__1200).clone().text();
+ /* let =noReferrer() = cs__1201 */
+ /* out(serviceWorkerSendHttpResponse(b), (u, cs__1200, cs__1201, sw__foo, sw__corr)) */
+ var __response = (cs__1200)
+ if (cs__1201)
+ __response = await appendHeader(__response, 'referrer-policy', cs__1201)
+ return cleanResponse(__response);
+ } catch (e) {
+ console.log(e)
+ console.log('Match error: '+event.request.url)
+ return errorResponse('match error!');
+ }
+ })());
+ } else {
+ /* event forwardRequest() */
+ }
+ }
+ } catch (e) {}
+});
+
diff --git a/casestudies-src/artificial_rp/extensions/sw_monitor/sw.pv b/casestudies-src/artificial_rp/extensions/sw_monitor/sw.pv
new file mode 100644
index 0000000..424aff0
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/sw_monitor/sw.pv
@@ -0,0 +1,21 @@
+
+let MonitorSW(b:Browser, h:Host, fb:Host) =
+ in(serviceWorkerFetch(b), (u:Uri, cs__1000, sw__ref:Uri, sw__p:Page, sw__aj:Ajax));
+ let (=httpGet()) = cs__1000 in
+ let (uri(=https(), =h, =loginpath(), =nullParams())) = u in
+ (out(rawRequest(b), (u, cs__1000, sw__ref, sw__p, sw__aj));
+ in(serviceWorkerResult(b), (=u, cs__1100:HttpResponse, cs__1101:ReferrerPolicy, sw__foo:XDR, sw__corr:bitstring));
+ let (httpOk(pagewithlink(uri(=https(), =fb, =oauthpath(), codereqparams(=appid, =uri(https(), h, callbackpath(), nullParams()), state))))) = cs__1100 in
+ let (=unsafeUrl()) = cs__1101 in
+ insert MRPSessions(state);
+ out(serviceWorkerSendHttpResponse(b), (u, cs__1100, cs__1101, sw__foo, sw__corr)))
+ else let (uri(=https(), =h, =callbackpath(), coderesparams(code, state))) = u in
+ (get MRPSessions(=state) in
+ out(rawRequest(b), (u, cs__1000, sw__ref, sw__p, sw__aj));
+ in(serviceWorkerResult(b), (=u, cs__1200:HttpResponse, cs__1201:ReferrerPolicy, sw__foo:XDR, sw__corr:bitstring));
+ let (=httpOk(success())) = cs__1200 in
+ let (=noReferrer()) = cs__1201 in
+ out(serviceWorkerSendHttpResponse(b), (u, cs__1200, cs__1201, sw__foo, sw__corr)))
+ else event forwardRequest().
+
+
diff --git a/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_auth_server.js b/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_auth_server.js
new file mode 100644
index 0000000..843ade6
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_auth_server.js
@@ -0,0 +1,28 @@
+
+const h = "integrator.com"
+const fb = "auth-server.com"
+const loginpath = "/login"
+const callbackpath = "/as-verify"
+const oauthpath = "/oauth2/auth"
+const appid = "67538654696"
+
+let db = new zango.Db('SW', { MRPSessions: ['col_1'] })
+let MRPSessions = db.collection('MRPSessions')
+
+const codereqparams = (qs) => {
+ let params = parseQuery(qs)
+ return [
+ params['client_id'],
+ new URL(params['redirect_uri']),
+ params['state']
+ ]
+}
+
+const coderesparams = (qs) => {
+ let params = parseQuery(qs)
+ return [
+ params['code'],
+ params['state']
+ ]
+}
+
diff --git a/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_facebook.js b/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_facebook.js
new file mode 100644
index 0000000..5cf838c
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_facebook.js
@@ -0,0 +1,28 @@
+
+const h = "integrator.com"
+const fb = "www.facebook.com"
+const loginpath = "/login"
+const callbackpath = "/fb-callback"
+const oauthpath = "/v3.2/dialog/oauth"
+const appid = "390639234906615"
+
+let db = new zango.Db('SW', { MRPSessions: ['col_1'] })
+let MRPSessions = db.collection('MRPSessions')
+
+const codereqparams = (qs) => {
+ let params = parseQuery(qs)
+ return [
+ params['client_id'],
+ new URL(params['redirect_uri']),
+ params['state']
+ ]
+}
+
+const coderesparams = (qs) => {
+ let params = parseQuery(qs)
+ return [
+ params['code'],
+ params['state']
+ ]
+}
+
diff --git a/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_google.js b/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_google.js
new file mode 100644
index 0000000..c3cd50c
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_google.js
@@ -0,0 +1,28 @@
+
+const h = "integrator.com"
+const fb = "accounts.google.com"
+const loginpath = "/login"
+const callbackpath = "/google-verify"
+const oauthpath = "/o/oauth2/auth"
+const appid = "996805361802-2tbmasivof6joftmsfqtgcuehpuokl71.apps.googleusercontent.com"
+
+let db = new zango.Db('SW', { MRPSessions: ['col_1'] })
+let MRPSessions = db.collection('MRPSessions')
+
+const codereqparams = (qs) => {
+ let params = parseQuery(qs)
+ return [
+ params['client_id'],
+ new URL(params['redirect_uri']),
+ params['state']
+ ]
+}
+
+const coderesparams = (qs) => {
+ let params = parseQuery(qs)
+ return [
+ params['code'],
+ params['state']
+ ]
+}
+
diff --git a/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_vk.js b/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_vk.js
new file mode 100644
index 0000000..8d73582
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/sw_monitor/sw_config_vk.js
@@ -0,0 +1,28 @@
+
+const h = "integrator.com"
+const fb = "oauth.vk.com"
+const loginpath = "/login"
+const callbackpath = "/vk-verify"
+const oauthpath = "/authorize"
+const appid = "7054259"
+
+let db = new zango.Db('SW', { MRPSessions: ['col_1'] })
+let MRPSessions = db.collection('MRPSessions')
+
+const codereqparams = (qs) => {
+ let params = parseQuery(qs)
+ return [
+ params['client_id'],
+ new URL(params['redirect_uri']),
+ params['state']
+ ]
+}
+
+const coderesparams = (qs) => {
+ let params = parseQuery(qs)
+ return [
+ params['code'],
+ params['state']
+ ]
+}
+
diff --git a/casestudies-src/artificial_rp/extensions/sw_monitor/sw_include.js b/casestudies-src/artificial_rp/extensions/sw_monitor/sw_include.js
new file mode 100644
index 0000000..8176daf
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/sw_monitor/sw_include.js
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////
+// LIB
+
+self.importScripts("https://unpkg.com/zangodb@1.0.7/dist/zangodb.min.js")
+
+const parseQuery = (n) => n.replace('?','').split('&').reduce((s,c) => {
+ let t = c.split('=');
+ s[t[0]] = decodeURIComponent(t[1]); return s;
+},{})
+
+function errorResponse(error) {
+ return new Response("
Error
"+ error +"", {
+ status: 418,
+ statusText: "I'm a teapot",
+ headers: {
+ 'Content-Type': 'text/html'
+ }
+ });
+}
+
+async function appendHeader(response, h, v) {
+ const newHeaders = new Headers(response.headers);
+ newHeaders.set(h, v);
+ let body = await response.blob()
+ return new Response(body, {
+ status: response.status,
+ statusText: response.statusText,
+ headers: newHeaders
+ })
+}
+
+const unsafeUrl = 'unsafe-url'
+const noReferrer = 'no-referrer'
+
+//function MatchFail() {}
+MatchFail = Error
+
+
+
+function cleanResponse(response) {
+ const clonedResponse = response.clone();
+
+ // Not all browsers support the Response.body stream, so fall back to reading
+ // the entire body into memory as a blob.
+ const bodyPromise = 'body' in clonedResponse ?
+ Promise.resolve(clonedResponse.body) :
+ clonedResponse.blob();
+
+ return bodyPromise.then((body) => {
+ // new Response() is happy when passed either a stream or a Blob.
+ return new Response(body, {
+ headers: clonedResponse.headers,
+ status: clonedResponse.status,
+ statusText: clonedResponse.statusText,
+ });
+ });
+}
diff --git a/casestudies-src/artificial_rp/extensions/vk_sso/autoload.php b/casestudies-src/artificial_rp/extensions/vk_sso/autoload.php
new file mode 100644
index 0000000..b22a577
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/vk_sso/autoload.php
@@ -0,0 +1,19 @@
+registerExtension([
+ 'name' => 'vk_sso',
+ 'tags' => ['vk-login'],
+ 'depends_on' => [],
+], function () {
+ // register or modify components
+ ComponentsManager::instance()->registerComponent(
+ 'routes', 'vkcallback', new vk_sso\VKCallback());
+ ComponentsManager::instance()->registerComponent(
+ 'login-button', 'vkbutton', new vk_sso\VKLoginButton());
+});
+
diff --git a/casestudies-src/artificial_rp/extensions/vk_sso/callback.php b/casestudies-src/artificial_rp/extensions/vk_sso/callback.php
new file mode 100644
index 0000000..76c16bb
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/vk_sso/callback.php
@@ -0,0 +1,39 @@
+respond(array('GET','POST'), $this->callback_path, function ($req,$res,$service) {
+ return $this->callback($service);
+ });
+ }
+
+ public function callback($service){
+ global $vk_config;
+ global $vk_oauth;
+
+ if (!isset($_GET['code']) || !isset($_GET['state']))
+ die("Error: bad request.");
+
+ if ($_GET['state'] !== $_SESSION['vk_state'])
+ die("Error: state matching error.");
+
+ $code = $_GET['code'];
+ $resp = $vk_oauth->getAccessToken(
+ $vk_config['client_id'], $vk_config['secret_key'], $this->red_uri, $code);
+ if (!isset($resp['access_token'])){
+ var_dump($resp);
+ die('Error: invalid response.');
+ }
+
+ $_SESSION['vk_access_token'] = $resp['access_token'];
+ $service->render(__DIR__.'/template_success.php', ['access_token' => $resp['access_token']]);
+ }
+}
diff --git a/casestudies-src/artificial_rp/extensions/vk_sso/config.php b/casestudies-src/artificial_rp/extensions/vk_sso/config.php
new file mode 100644
index 0000000..660b047
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/vk_sso/config.php
@@ -0,0 +1,28 @@
+ '7054259',
+ 'secret_key' => '2K3HEOyYN68Qy6wSZasW'
+];
+
+
+$vk_oauth = new \VK\OAuth\VKOAuth();
+
+// Set custom proxy options using reflection
+if (getenv('HTTP_PROXY') && getenv('HTTP_PROXY_PORT')) {
+ $hc_ref = new \ReflectionProperty("\VK\OAuth\VKOauth", "http_client");
+ $hc_ref->setAccessible(true);
+ $curl = $hc_ref->getValue($vk_oauth);
+ $io_ref = new \ReflectionProperty("\VK\TransportClient\Curl\CurlHttpClient", "initial_opts");
+ $io_ref->setAccessible(true);
+ $opts = $io_ref->getValue($curl);
+
+ $opts[CURLOPT_SSL_VERIFYHOST] = 0;
+ $opts[CURLOPT_SSL_VERIFYPEER] = 0;
+ $opts[CURLOPT_PROXY] = getenv('HTTP_PROXY');
+ $opts[CURLOPT_PROXYPORT] = getenv('HTTP_PROXY_PORT');
+
+ $io_ref->setValue($curl, $opts);
+}
diff --git a/casestudies-src/artificial_rp/extensions/vk_sso/login_button.php b/casestudies-src/artificial_rp/extensions/vk_sso/login_button.php
new file mode 100644
index 0000000..15a67c5
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/vk_sso/login_button.php
@@ -0,0 +1,27 @@
+getAuthorizeUrl(
+ \VK\OAuth\VKOAuthResponseType::CODE,
+ $vk_config['client_id'], $this->red_uri,
+ \VK\OAuth\VKOAuthDisplay::PAGE, $this->scopes, $state);
+
+ return sprintf(
+ '
'.
+ ''.
+ 'Log in with VK!', $login_uri);
+ }
+}
diff --git a/casestudies-src/artificial_rp/extensions/vk_sso/template_success.php b/casestudies-src/artificial_rp/extensions/vk_sso/template_success.php
new file mode 100644
index 0000000..458248b
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/vk_sso/template_success.php
@@ -0,0 +1,10 @@
+
+
+
+
+
You have been successfully logged in:
+
Access Token: access_token; ?>
+
+
diff --git a/casestudies-src/artificial_rp/extensions/vk_state_check_vuln/autoload.php b/casestudies-src/artificial_rp/extensions/vk_state_check_vuln/autoload.php
new file mode 100644
index 0000000..2c4bb75
--- /dev/null
+++ b/casestudies-src/artificial_rp/extensions/vk_state_check_vuln/autoload.php
@@ -0,0 +1,24 @@
+registerExtension([
+ 'name' => 'vk_sso_state_check_vuln',
+ 'tags' => ['vk-state-check-vuln'],
+ 'depends_on' => ['vk-login']
+ ], function () {
+ ComponentsManager
+ ::instance()
+ ->registerComponent('routes', 'vkcallback', new vk_sso\FlawedVKCallback());
+ });
+}
diff --git a/casestudies-src/artificial_rp/includes/extensions.inc.php b/casestudies-src/artificial_rp/includes/extensions.inc.php
new file mode 100644
index 0000000..c15adbd
--- /dev/null
+++ b/casestudies-src/artificial_rp/includes/extensions.inc.php
@@ -0,0 +1,89 @@
+extensions,$ext);
+ }
+
+ public function installExtensions() {
+ // make sure all depends are on tags
+ $all_tags = array_unique(array_flatmap(
+ function ($e) { return $e['tags']; }, $this->extensions));
+ $all_depends = array_unique(array_flatmap(function ($e) {
+ if (!isset($e['depends_on'])) return [];
+ return $e['depends_on'];
+ }, $this->extensions));
+
+ if (array_intersect($all_depends, $all_tags) != $all_depends)
+ die("InstallExtension error: unknown tags ".array_diff($all_depends, $all_tags));
+
+ $installed_tags = array();
+ while (!empty($this->extensions)) {
+ $e = array_pop($this->extensions);
+
+ if (!isset($e['depends_on']) ||
+ array_product(array_map(
+ function ($c) use ($e, $installed_tags) {
+ return in_array($c, $installed_tags); }, $e['depends_on']))) {
+ $installed_tags = array_merge($installed_tags, $e['tags']);
+ $e['install_fn']();
+ } else {
+ array_unshift($this->extensions, $e);
+ }
+ }
+ }
+}
+
+
+abstract class Component {
+ abstract public function render();
+}
+
+class ComponentsManager {
+
+ private $components = array();
+
+ public static function instance()
+ {
+ static $inst = null;
+ if ($inst === null) {
+ $inst = new ComponentsManager();
+ }
+ return $inst;
+ }
+ private function __construct() {}
+
+ public function registerComponent($where,$name, $c) {
+ if (!$this->components[$where])
+ $this->components[$where] = array($name => $c);
+ else
+ $this->components[$where][$name] = $c;
+ }
+
+ public function renderComponents($where) {
+ if ($this->components) {
+ return array_map(function ($c) { return $c->render(); }, $this->components[$where]);
+ }
+ return [];
+ }
+
+ public function getComponent($where,$name) {
+ return $this->components[$where][$name];
+ }
+}
diff --git a/casestudies-src/artificial_rp/includes/utils.inc.php b/casestudies-src/artificial_rp/includes/utils.inc.php
new file mode 100644
index 0000000..0b527dd
--- /dev/null
+++ b/casestudies-src/artificial_rp/includes/utils.inc.php
@@ -0,0 +1,78 @@
+updateOne(
+ array("username" => $user),
+ array('$set' => array('fb_user_id' => $idpId, "fb_access_token" => $accessToken)),
+ );
+
+ $document = array($idpName.'_user_id' => (string) $idpId);
+ $cursor = $users->find($document);
+ $it = new \IteratorIterator($cursor);
+ $it->rewind();
+ while($user = $it->current()){
+ $_SESSION["username"] = $user['username'];
+ // User is logged in
+ $_SESSION["loggedin"] = true;
+ // User is logged in into IdP
+ $_SESSION[$idpName."_loggedin"] = true;
+ $_SESSION[$idpName."_user_id"] = $idpId;
+ $_SESSION[$idpName."_access_token"] = $accessToken;
+ return true;
+ }
+ return false;
+}
+
+function register_with_access_token($user, $accessToken, $idpId, $idpName) {
+ global $users;
+
+ $document = array('username' => (string) $user);
+ if ($users->count($document) > 0){
+ return false;
+ }
+ //$document['password'] = (string) sha1($password);
+ $document[$idpName.'_access_token'] = (string) $accessToken;
+ $document[$idpName.'_user_id'] = (string) $idpId;
+ $users->insertOne($document);
+ return true;
+}
+
+function login_with_password($user, $password) {
+ global $users;
+ $document = array('username' => (string) $user);
+ $cursor = $users->find($document);
+ $it = new \IteratorIterator($cursor);
+ $it->rewind();
+ while($user = $it->current()){
+ if((string) sha1($password) == $user['password']) {
+ $_SESSION["username"] = $user['username'];
+ // User is logged in
+ $_SESSION["loggedin"] = true;
+ return true;
+ }
+ }
+ return false;
+}
+
+function register_with_password($user, $password) {
+ global $users;
+
+ $document = array('username' => (string) $user);
+ if ($users->count($document) > 0){
+ return false;
+ }
+ $document['password'] = (string) sha1($password);
+ $users->insertOne($document);
+ return true;
+}
diff --git a/casestudies-src/artificial_rp/index.php b/casestudies-src/artificial_rp/index.php
new file mode 100644
index 0000000..3d36caf
--- /dev/null
+++ b/casestudies-src/artificial_rp/index.php
@@ -0,0 +1,87 @@
+sso->users;
+$klein = new \Klein\Klein();
+
+
+////////////////////////////////////////////////////////////////////////////////
+// EXTENSIONS
+
+// Require init files from extensions directory
+$exts = glob(__DIR__ . '/extensions/*/autoload.php');
+foreach($exts as $ext) {
+ require($ext);
+}
+ExtensionsManager::instance()->installExtensions();
+
+
+////////////////////////////////////////////////////////////////////////////////
+// APP
+
+// Set the layout on all routes
+$klein->respond(array('GET','POST'), '*', function ($req, $res, $service) {
+ $service->layout(__DIR__.'/templates/layout.php');
+});
+
+$klein->respond(array('GET','POST'), '/login', function ($req, $res, $service) {
+ if ($req->method('post')) {
+ if(login_with_password($req->params()["username"], $req->params()["password"])){
+ // Login successful
+ $buttons = ComponentsManager::instance()->renderComponents('settings-button');
+ $service->render(__DIR__.'/templates/settings.php', ['buttons' => $buttons]);
+ header("Location: /settings");
+ return;
+ } else {
+
+ }
+ }
+
+ $buttons = ComponentsManager::instance()->renderComponents('login-button');
+ $service->render(__DIR__.'/templates/login.php', ['buttons' => $buttons]);
+});
+
+$klein->respond(array('GET','POST'), '/settings', function ($req, $res, $service) {
+ $buttons = ComponentsManager::instance()->renderComponents('settings-button');
+ $service->render(__DIR__.'/templates/settings.php', ['buttons' => $buttons]);
+});
+
+$klein->respond('GET', '/logout', function ($req, $res, $service) {
+ session_destroy();
+ header("Location: /");
+ return;
+});
+
+$klein->respond(array('GET','POST'), '/register', function ($req, $res, $service) {
+ if ($req->method('post')) {
+ if(register_with_password($req->params()["username"], $req->params()["password"])){
+ $service->render(__DIR__.'/templates/login.php');
+ } else {
+ $service->render(__DIR__.'/templates/register.php');
+ }
+ } else {
+ $service->render(__DIR__.'/templates/register.php');
+ }
+});
+
+
+$klein->respond('GET', '/', function ($req, $res, $service) {
+ $service->render(__DIR__.'/templates/home.php');
+});
+
+$klein->onHttpError(function ($code, $router) {
+ echo 'Error '.$code;
+});
+
+// Render all registered components that add routes to the app
+ComponentsManager::instance()->renderComponents('routes');
+
+$klein->dispatch();
diff --git a/casestudies-src/artificial_rp/static/style.css b/casestudies-src/artificial_rp/static/style.css
new file mode 100644
index 0000000..3b8cee4
--- /dev/null
+++ b/casestudies-src/artificial_rp/static/style.css
@@ -0,0 +1,43 @@
+html {
+ font-size: 14px;
+}
+
+.card-deck .card {
+ min-width: 220px;
+}
+
+.form-signin {
+ width: 100%;
+ max-width: 330px;
+ padding: 15px;
+ margin: auto;
+}
+.form-signin .form-control {
+ position: relative;
+ box-sizing: border-box;
+ height: auto;
+ padding: 10px;
+ font-size: 16px;
+}
+.form-signin .form-control:focus {
+ z-index: 2;
+}
+.form-signin input[type="email"].group {
+ margin-bottom: -1px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.form-signin input[type="password"].group {
+ margin-bottom: 10px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
+
+.form-signin input[type="password"] {
+ margin-bottom: 10px;
+}
+
+.titlelink, .titlelink:hover {
+ text-decoration: none;
+ color: inherit;
+}
diff --git a/casestudies-src/artificial_rp/templates/.keep b/casestudies-src/artificial_rp/templates/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/casestudies-src/artificial_rp/templates/home.php b/casestudies-src/artificial_rp/templates/home.php
new file mode 100644
index 0000000..30a5757
--- /dev/null
+++ b/casestudies-src/artificial_rp/templates/home.php
@@ -0,0 +1,11 @@
+
+
+
+
+
+
You might be interested in loggin in
+
Log In
+
+
diff --git a/casestudies-src/artificial_rp/templates/layout.php b/casestudies-src/artificial_rp/templates/layout.php
new file mode 100644
index 0000000..2a8cdcc
--- /dev/null
+++ b/casestudies-src/artificial_rp/templates/layout.php
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
Integrator
+
+
+
+
+
+
+
+
+
+
+
+ yieldView(); ?>
+
+
+
+
+
+
+
diff --git a/casestudies-src/artificial_rp/templates/login.php b/casestudies-src/artificial_rp/templates/login.php
new file mode 100644
index 0000000..f9d9107
--- /dev/null
+++ b/casestudies-src/artificial_rp/templates/login.php
@@ -0,0 +1,25 @@
+
+
diff --git a/casestudies-src/artificial_rp/templates/register.php b/casestudies-src/artificial_rp/templates/register.php
new file mode 100644
index 0000000..9d47b4e
--- /dev/null
+++ b/casestudies-src/artificial_rp/templates/register.php
@@ -0,0 +1,15 @@
+
+