diff --git a/.circleci/config.yml b/.circleci/config.yml index 2744cc841..402b05332 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,82 +5,36 @@ version: 2 jobs: publish: docker: - - image: circleci/node:9-browsers + - image: circleci/node:11.1 working_directory: /home/circleci/docs - environment: - - GITBOOK_DIR: /home/circleci/docs/.gitbook - - VERSION: latest steps: - checkout - - run: > - if [ -z ${CIRCLE_TAG+x} ]; then echo "Skipping tagging because not a tag"; else find ./guides/ -name "*.md" -type f -print0 | xargs -0 sed -i 's/'$(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1))'/'${CIRCLE_TAG}'/g'; fi - - run: yarn install - - run: yarn build - - run: git config --global user.email "hi@ory.am" + - run: cd website; npm ci + - run: git config --global user.email "hi@ory.sh" - run: git config --global user.name "ORY Continuous Integration" - run: "git clone https://arekkas:$DOCS_TOKEN_PUSH@github.com/ory/ory-am.git ../ory-am" + - run: cd website; npm run api + - run: cd website; npm build + - run: rm -rf ../ory-am/docs/ + - run: cp -R ./website/build/docs/* ../ory-am/docs/ - - run: "VERSION=${CIRCLE_TAG:-master}; mkdir -p ../ory-am/docs/guides/$VERSION/ || exit 0" - - run: "VERSION=${CIRCLE_TAG:-master}; mkdir -p ../ory-am/docs/apis/$VERSION/ || exit 0" - - - run: "VERSION=${CIRCLE_TAG:-master}; rm -rf ../ory-am/docs/guides/$VERSION/* || exit 0" - - run: "VERSION=${CIRCLE_TAG:-master}; rm -rf ../ory-am/docs/apis/$VERSION/* || exit 0" - - - run: "cp -R ./build/faq/* ../ory-am/docs/faq/" - - - run: "VERSION=${CIRCLE_TAG:-master}; cp -R ./build/guides/* ../ory-am/docs/guides/$VERSION/" - - run: "VERSION=${CIRCLE_TAG:-master}; cp -R ./apis/* ../ory-am/docs/apis/$VERSION/" - - - run: 'if [ -z ${CIRCLE_TAG+x} ]; then echo "Skipping push to guides latest"; else (rm -rf ../ory-am/docs/guides/latest; mkdir -p ../ory-am/docs/guides/latest; cp -R ./build/guides/* ../ory-am/docs/guides/latest); fi' - - run: 'if [ -z ${CIRCLE_TAG+x} ]; then echo "Skipping push to apis latest"; else (rm -rf ../ory-am/docs/apis/latest; mkdir -p ../ory-am/docs/apis/latest; cp -R ./apis/* ../ory-am/docs/apis/latest); fi' - - - run: "VERSION=${CIRCLE_TAG:-master}; cp -R ./build/guides/* ../ory-am/docs/guides/$VERSION/" - - run: "VERSION=${CIRCLE_TAG:-master}; cp -R ./apis/* ../ory-am/docs/apis/$VERSION/" - - - run: "(VERSION=${CIRCLE_TAG:-master}; cd ../ory-am && git add -A && git commit -a -m \"docs: Updates documentation version $VERSION\" && git push origin) || exit 0" - - tag: + test: docker: - - image: circleci/node:9-browsers + - image: circleci/node:11.1 working_directory: /home/circleci/docs steps: - checkout - - run: git config --global user.email "hi@ory.am" - - run: git config --global user.name "ORY Continuous Integration" - - run: - shell: /bin/bash - command: ./.circleci/tag.sh - - run: cat ./guides/book.json - - run: cat ./guides/package.json - - run: > - find ./guides/ -name "*.md" -type f -print0 | xargs -0 sed -i 's/'$(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1))'/'${CIRCLE_TAG}'/g' - - run: "git commit -a -m \"Updates guides to version ${CIRCLE_TAG}\"" - - run: git remote rm origin - - run: git remote add origin https://arekkas:$GITHUB_TOKEN@github.com/ory/docs.git - - run: git push origin HEAD:master + - run: cd website; npm ci + - run: ./scripts/test-links.sh workflows: version: 2 - "publish-master": - jobs: - - publish: - filters: - branches: - only: master - "publish-tag": + publish: jobs: + - test - publish: - filters: - branches: - ignore: /.*/ - tags: - only: /.*/ - - tag: -# This avoids race conditions (as both publish-tag and publish-master push to the website): requires: - - publish + - test filters: branches: - ignore: /.*/ - tags: - only: /.*/ + only: master diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..27d2dae2b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +*/node_modules +*.log diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..af67531d8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* -crlf diff --git a/.gitignore b/.gitignore index ebc401b61..8c38b3c0b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,15 @@ node_modules/ build/ _book/ +.DS_Store + +node_modules + +lib/core/metadata.js +lib/core/MetadataBlog.js + +website/translated_docs +website/build/ +website/yarn.lock +website/node_modules +website/i18n/* diff --git a/.widdershins/config.json b/.widdershins/config.json new file mode 100644 index 000000000..740e6dd3a --- /dev/null +++ b/.widdershins/config.json @@ -0,0 +1,24 @@ +{ + "language_tabs": [], + "verbose": true, + "codeSamples": true, + "httpSnippet": true, + "search": false, + "discovery": false, + "tocSummary": true, + "sample": true, + "tagGroups": [ + { + "title": "Public Endpoints", + "tags": [ + "public" + ] + }, + { + "title": "Administrative Endpoints", + "tags": [ + "admin" + ] + } + ] +} \ No newline at end of file diff --git a/.widdershins/templates/README.md b/.widdershins/templates/README.md new file mode 100644 index 000000000..6b196ae93 --- /dev/null +++ b/.widdershins/templates/README.md @@ -0,0 +1,63 @@ +## Swagger / OpenAPI 2 and OpenAPI 3 template parameters + +Note that properties of OpenAPI objects will be in OpenAPI 3.0 form, as +Swagger / OpenAPI 2.0 definitions are converted automatically. + +### Code templates + +* `method` - the HTTP method of the operation (in lower-case) +* `methodUpper` - the HTTP method of the operation (in upper-case) +* `url` - the full URL of the operation (including protocol and host) +* `consumes[]` - an array of MIME-types the operation consumes +* `produces[]` - an array of MIME-types the operation produces +* `operation` - the current operation object +* `operationId` - the current operation id +* `opName` - the operationId if set, otherwise the method + path +* `tags[]` - the full list of tags applying to the operation +* `security` - the security definitions applying to the operation +* `resource` - the current tag/path object +* `parameters[]` - an array of parameters for the operation (see below) +* `queryString` - an example queryString, urlEncoded +* `requiredQueryString` - an example queryString for `required:true` parameters +* `queryParameters[]` - a subset of `parameters` that are `in:query` +* `requiredParameters[]` - a subset of `queryParameters` that are `required:true` +* `headerParameters[]` - a subset of `parameters` that are `in:header` +* `allHeaders[]` - a concatenation of `headerParameters` and pseudo-parameters `Accept` and `Content-Type`, and optionally `Authorization` (the latter has an `isAuth` boolean property set true so it can be omitted in templates if desired + +### Parameter template + +* `parameters[]` - an array of [parameters](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#parameterObject), including the following pseudo-properties + * `shortDesc` - a truncated version of the parameter description + * `safeType` - a computed version of the parameter type, including Body and schema names + * `originalType` - the original type of the parameter + * `exampleValues` - an object containing examples for use in code-templates + * `json` - example values in JSON compatible syntax + * `object` - example values in raw object form (unquoted strings etc) + * `depth` - a zero-based indicator of the depth of expanded request body parameters +* `enums[]` - an array of (parameter)name/value pairs + +### Responses template + +* `responses[]` - an array of [responses](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#responseObject), including `status` and `meaning` properties + +### Authentication template + +* `authenticationStr` - a simple string of methods (and scopes where appropriate) +* `securityDefinitions[]` - an array of applicable [securityDefinitions](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#securityRequirementObject) + +### Schema Property template + +* `schemaProperties[]` - an array of + * `name` + * `type` + * `required` + * `description` +* `enums[]` - an array of (schema property)name/value pairs + +### Common to all templates + +* `openapi` - the top-level OpenAPI / Swagger document +* `header` - the front-matter of the Slate/Shins markdown document +* `host` - the (computed) host of the API +* `protocol` - the default/first protocol of the API +* `baseUrl` - the (computed) baseUrl of the API (including protocol and host) diff --git a/.widdershins/templates/authentication.def b/.widdershins/templates/authentication.def new file mode 100644 index 000000000..6b039e532 --- /dev/null +++ b/.widdershins/templates/authentication.def @@ -0,0 +1,5 @@ + + diff --git a/.widdershins/templates/authentication_none.def b/.widdershins/templates/authentication_none.def new file mode 100644 index 000000000..ddb19cf27 --- /dev/null +++ b/.widdershins/templates/authentication_none.def @@ -0,0 +1,3 @@ + diff --git a/.widdershins/templates/callbacks.def b/.widdershins/templates/callbacks.def new file mode 100644 index 000000000..4f155e8a1 --- /dev/null +++ b/.widdershins/templates/callbacks.def @@ -0,0 +1,38 @@ +{{? data.operation.callbacks}} + +#### Callbacks + +
+ +{{ data.oldOperation = data.operation; }} + +{{ for (var c in data.operation.callbacks) { }} + +##### {{=c}} + +{{ var callback = data.operation.callbacks[c]; }} + +{{ for (var e in callback) { }} + +**{{=e}}** + +{{ var exp = callback[e]; }} + +{{ for (var m in exp) { }} + +{{ data.operation = exp[m]; }} +{{ data.method.operation = data.operation; }} + +{{= data.templates.operation(data) }} + +{{ } /* of methods */ }} + +{{ } /* of expressions */ }} + +{{ } /* of callbacks */ }} + +{{ data.operation = data.oldOperation; }} + +
+ +{{?}} diff --git a/.widdershins/templates/code_go.dot b/.widdershins/templates/code_go.dot new file mode 100644 index 000000000..4b68a5490 --- /dev/null +++ b/.widdershins/templates/code_go.dot @@ -0,0 +1,23 @@ +package main + +import ( + "bytes" + "net/http" +) + +func main() { + +{{?data.allHeaders.length}} + headers := map[string][]string{ + {{~data.allHeaders :p:index}}"{{=p.name}}": []string{"{{=p.exampleValues.object}}"}, + {{~}} + }{{?}} + + data := bytes.NewBuffer([]byte{jsonReq}) + req, err := http.NewRequest("{{=data.methodUpper}}", "{{=data.url}}", data) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} diff --git a/.widdershins/templates/code_http.dot b/.widdershins/templates/code_http.dot new file mode 100644 index 000000000..98636ec86 --- /dev/null +++ b/.widdershins/templates/code_http.dot @@ -0,0 +1,7 @@ +{{=data.methodUpper}} {{=data.url}}{{=data.requiredQueryString}} HTTP/1.1{{? data.host}} +Host: {{=data.host}}{{?}}{{?data.consumes.length}} +Content-Type: {{=data.consumes[0]}}{{?}}{{?data.produces.length}} +Accept: {{=data.produces[0]}}{{?}} +{{?data.headerParameters.length}}{{~ data.headerParameters :p:index}}{{=p.name}}: {{=p.exampleValues.object}} +{{~}} +{{?}} \ No newline at end of file diff --git a/.widdershins/templates/code_java.dot b/.widdershins/templates/code_java.dot new file mode 100644 index 000000000..8d61d00c6 --- /dev/null +++ b/.widdershins/templates/code_java.dot @@ -0,0 +1,13 @@ +URL obj = new URL("{{=data.url}}{{=data.requiredQueryString}}"); +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("{{=data.methodUpper}}"); +int responseCode = con.getResponseCode(); +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream())); +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); +System.out.println(response.toString()); diff --git a/.widdershins/templates/code_javascript.dot b/.widdershins/templates/code_javascript.dot new file mode 100644 index 000000000..06d264133 --- /dev/null +++ b/.widdershins/templates/code_javascript.dot @@ -0,0 +1,14 @@ +{{?data.allHeaders.length}}var headers = { +{{~data.allHeaders :p:index}} '{{=p.name}}':{{=p.exampleValues.json}}{{?index < data.allHeaders.length-1}},{{?}} +{{~}} +}; +{{?}} +$.ajax({ + url: '{{=data.url}}', + method: '{{=data.method.verb}}', +{{?data.requiredQueryString}} data: '{{=data.requiredQueryString}}',{{?}} +{{?data.allHeaders.length}} headers: headers,{{?}} + success: function(data) { + console.log(JSON.stringify(data)); + } +}) diff --git a/.widdershins/templates/code_nodejs.dot b/.widdershins/templates/code_nodejs.dot new file mode 100644 index 000000000..44ea2a5e6 --- /dev/null +++ b/.widdershins/templates/code_nodejs.dot @@ -0,0 +1,18 @@ +const fetch = require('node-fetch'); +{{?data.bodyParameter.present}}const inputBody = '{{=data.bodyParameter.exampleValues.json}}';{{?}} +{{?data.allHeaders.length}}const headers = { +{{~data.allHeaders :p:index}} '{{=p.name}}':{{=p.exampleValues.json}}{{?index < data.allHeaders.length-1}},{{?}} +{{~}} +}; +{{?}} +fetch('{{=data.url}}{{=data.requiredQueryString}}', +{ + method: '{{=data.methodUpper}}'{{?data.bodyParameter.present || data.allHeaders.length}},{{?}} +{{?data.bodyParameter.present}} body: inputBody{{?}}{{? data.bodyParameter.present && data.allHeaders.length}},{{?}} +{{?data.allHeaders.length}} headers: headers{{?}} +}) +.then(function(res) { + return res.json(); +}).then(function(body) { + console.log(body); +}); diff --git a/.widdershins/templates/code_python.dot b/.widdershins/templates/code_python.dot new file mode 100644 index 000000000..bd277a1db --- /dev/null +++ b/.widdershins/templates/code_python.dot @@ -0,0 +1,10 @@ +import requests +{{?data.allHeaders.length}}headers = { +{{~data.allHeaders :p:index}} '{{=p.name}}': {{=p.exampleValues.json}}{{?index < data.allHeaders.length-1}},{{?}} +{{~}}} +{{?}} +r = requests.{{=data.method.verb}}('{{=data.url}}', params={ +{{~ data.requiredParameters :p:index}} '{{=p.name}}': {{=p.exampleValues.json}}{{? data.requiredParameters.length-1 != index }},{{?}}{{~}} +{{?data.allHeaders.length}}}, headers = headers{{?}}) + +print r.json() diff --git a/.widdershins/templates/code_ruby.dot b/.widdershins/templates/code_ruby.dot new file mode 100644 index 000000000..172219db1 --- /dev/null +++ b/.widdershins/templates/code_ruby.dot @@ -0,0 +1,14 @@ +require 'rest-client' +require 'json' + +{{?data.allHeaders.length}}headers = { +{{~data.allHeaders :p:index}} '{{=p.name}}' => {{=p.exampleValues.json}}{{?index < data.allHeaders.length-1}},{{?}} +{{~}}}{{?}} + +result = RestClient.{{=data.method.verb}} '{{=data.url}}', + params: { + {{~ data.requiredParameters :p:index}}'{{=p.name}}' => '{{=p.safeType}}'{{? data.requiredParameters.length-1 != index }},{{?}} +{{~}}}{{? data.allHeaders.length}}, headers: headers +{{?}} + +p JSON.parse(result) diff --git a/.widdershins/templates/code_shell.dot b/.widdershins/templates/code_shell.dot new file mode 100644 index 000000000..993469280 --- /dev/null +++ b/.widdershins/templates/code_shell.dot @@ -0,0 +1,3 @@ +curl -X {{=data.methodUpper}} {{=data.url}}{{=data.requiredQueryString}}{{?data.allHeaders.length}} \{{?}} +{{~data.allHeaders :p:index}} -H '{{=p.name}}: {{=p.exampleValues.object}}'{{?index < data.allHeaders.length-1}} \{{?}} +{{~}} diff --git a/.widdershins/templates/debug.def b/.widdershins/templates/debug.def new file mode 100644 index 000000000..04fcf8f76 --- /dev/null +++ b/.widdershins/templates/debug.def @@ -0,0 +1 @@ +{{= data.utils.inspect(data) }} diff --git a/.widdershins/templates/discovery.def b/.widdershins/templates/discovery.def new file mode 100644 index 000000000..4755a7b8a --- /dev/null +++ b/.widdershins/templates/discovery.def @@ -0,0 +1,11 @@ + diff --git a/.widdershins/templates/footer.def b/.widdershins/templates/footer.def new file mode 100644 index 000000000..139597f9c --- /dev/null +++ b/.widdershins/templates/footer.def @@ -0,0 +1,2 @@ + + diff --git a/.widdershins/templates/links.def b/.widdershins/templates/links.def new file mode 100644 index 000000000..72426c268 --- /dev/null +++ b/.widdershins/templates/links.def @@ -0,0 +1,18 @@ +{{? data.response.links }} + +##### Links + +{{ for (var l in data.response.links) { }} +{{ var link = data.response.links[l]; }} + +**{{=l}}** => {{?link.operationId}}{{=link.operationId}}{{??}}{{=link.operationRef}}{{?}} + +{{? link.parameters }} +|Parameter|Expression| +|---|---| +{{for (var p in link.parameters) { }}|{{=p}}|{{=link.parameters[p]}}|{{ } }} +{{?}} + +{{ } /* of links */ }} + +{{?}} diff --git a/.widdershins/templates/main.dot b/.widdershins/templates/main.dot new file mode 100644 index 000000000..06ee10923 --- /dev/null +++ b/.widdershins/templates/main.dot @@ -0,0 +1,109 @@ +{{? data.api.info.description}}{{=data.api.info.description}}{{?}} + +> You are viewing a REST API documentation. This documentation is auto-generated from a swagger specification which +itself is generated from annotations in the source files of the project. It is possible that this documentation includes +bugs and that code samples are incomplete or wrong. +> +> If you find issues in the respective documentation, please do not edit the +markdown files directly (as they are generated) but raise an issue on the project's GitHub instead. This documentation +will improve over time with your help! If you have ideas how to improve this part of the documentation, feel free to +share them in a [GitHub issue](https://github.com/ory/docs/issues/new) any time. + +{{? data.api.components && data.api.components.securitySchemes }} +{{#def.security}} +{{?}} + +{{ for (var r in data.resources) { }} +{{ data.resource = data.resources[r]; }} + + +## {{= r}} + +{{? data.resource.description }}{{= data.resource.description}}{{?}} + +{{? data.resource.externalDocs}} +{{=data.resource.externalDocs.description||'External documentation'}} +{{?}} + +{{ for (var m in data.resource.methods) { }} +{{ data.operationUniqueName = m; }} +{{ data.method = data.resource.methods[m]; }} +{{ data.operationUniqueSlug = data.method.slug; }} +{{ data.operation = data.method.operation; }} +{{= data.templates.operation(data) }} +{{ } /* of methods */ }} + +{{ } /* of resources */ }} + +{{? data.api.components && data.api.components.schemas }} +## Schemas + +{{ for (var s in data.components.schemas) { }} +{{ var origSchema = data.components.schemas[s]; }} +{{ var schema = data.api.components.schemas[s]; }} + +{{=s}} +#### {{=s}} + + + +{{? data.options.yaml }} +```yaml +{{=data.utils.yaml.safeDump(data.utils.getSample(schema,data.options,{},data.api))}} +{{??}} +```json +{{=data.utils.safejson(data.utils.getSample(schema,data.options,{},data.api),null,2)}} +{{?}} +``` + +{{ var enums = []; }} +{{ var blocks = data.utils.schemaToArray(origSchema,-1,{trim:true,join:true},data); }} +{{ for (var block of blocks) { + for (var p of block.rows) { + if (p.schema && p.schema.enum) { + for (var e of p.schema.enum) { + enums.push({name:p.name,value:e}); + } + } + } + } +}} + +{{~ blocks :block}} +{{? block.title }}*{{= block.title}}*{{= '\n\n'}}{{?}} +{{? block.externalDocs}} +{{=block.externalDocs.description||'External documentation'}} +{{?}} + +{{? block===blocks[0] }} +#### Properties +{{?}} + +{{? block.rows.length}}|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---|{{?}} +{{~ block.rows :p}}|{{=p.displayName}}|{{=p.safeType}}|{{=p.required}}|{{=p.restrictions||'none'}}|{{=p.description||'none'}}| +{{~}} +{{~}} +{{? (blocks[0].rows.length === 0) && (blocks.length === 1) }} +*None* +{{?}} + +{{? enums.length > 0 }} +##### Enumerated Values + +|Property|Value| +|---|---| +{{~ enums :e}}|{{=e.name}}|{{=data.utils.toPrimitive(e.value)}}| +{{~}} + +{{?}} + +{{ } /* of schemas */ }} + +{{?}} + +{{#def.footer}} + +{{? data.options.discovery}} +{{#def.discovery}} +{{?}} diff --git a/.widdershins/templates/operation.dot b/.widdershins/templates/operation.dot new file mode 100644 index 000000000..32ad3d3cf --- /dev/null +++ b/.widdershins/templates/operation.dot @@ -0,0 +1,194 @@ + +{{? data.operation.operationId}} + +{{?}} + +{{ data.methodUpper = data.method.verb.toUpperCase(); }} +{{ data.url = data.utils.slashes(data.baseUrl + data.method.path); }} +{{ data.parameters = data.operation.parameters; }} +{{ data.enums = []; }} +{{ data.utils.fakeProdCons(data); }} +{{ data.utils.fakeBodyParameter(data); }} +{{ data.utils.mergePathParameters(data); }} +{{ data.utils.getParameters(data); }} + +### {{= data.operationUniqueName}} + +``` +{{=data.methodUpper}} {{=data.url}}{{=data.requiredQueryString}} HTTP/1.1{{? data.host}} +Host: {{=data.host}}{{?}}{{?data.consumes.length}} +Content-Type: {{=data.consumes[0]}}{{?}}{{?data.produces.length}} +Accept: {{=data.produces[0]}}{{?}} +{{?data.headerParameters.length}}{{~ data.headerParameters :p:index}}{{=p.name}}: {{=p.exampleValues.object}} +{{~}} +{{?}} +``` + +{{? data.operation.summary && !data.options.tocSummary}}*{{= data.operation.summary }}*{{?}} + +{{? data.operation.description}}{{= data.operation.description }}{{?}} + +{{? data.operation.requestBody}} +#### Request body + +{{? data.bodyParameter.exampleValues.description }} +##### {{= data.bodyParameter.exampleValues.description }} +{{?}} + +{{= data.utils.getBodyParameterExamples(data) }} +{{?}} + +{{? data.parameters && data.parameters.length }} +{{#def.parameters}} +{{?}} + +{{#def.responses}} + +{{#def.callbacks}} + +{{ data.security = data.operation.security ? data.operation.security : data.api.security; }} +{{? data.security && data.security.length }} +{{#def.authentication}} +{{??}} +{{#def.authentication_none}} +{{?}} + +{{? data.options.codeSamples || data.operation["x-code-samples"] }} +#### Code samples + +
+ +
+
+ +```shell +curl -X {{=data.methodUpper}} {{=data.url}}{{=data.requiredQueryString}}{{?data.allHeaders.length}} \{{?}} +{{~data.allHeaders :p:index}} -H '{{=p.name}}: {{=p.exampleValues.object}}'{{?index < data.allHeaders.length-1}} \{{?}}{{~}} +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() {{{?data.allHeaders.length}} + headers := map[string][]string{ {{~data.allHeaders :p:index}} + "{{=p.name}}": []string{"{{=p.exampleValues.object}}"},{{~}} + }{{?}} + + var body []byte + // body = ... + + req, err := http.NewRequest("{{=data.methodUpper}}", "{{=data.url}}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +{{?data.bodyParameter.present}}const input = '{{=data.bodyParameter.exampleValues.json}}';{{?}} +{{?data.allHeaders.length}}const headers = { +{{~data.allHeaders :p:index}} '{{=p.name}}': {{=p.exampleValues.json}}{{?index < data.allHeaders.length-1}},{{?}}{{~}} +} +{{?}} +fetch('{{=data.url}}{{=data.requiredQueryString}}', { + method: '{{=data.methodUpper}}'{{?data.bodyParameter.present || data.allHeaders.length}},{{?}}{{?data.bodyParameter.present}} + body: input{{?}}{{? data.bodyParameter.present && data.allHeaders.length}},{{?}}{{?data.allHeaders.length}} + headers{{?}} +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("{{=data.url}}{{=data.requiredQueryString}}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("{{=data.methodUpper}}"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +{{?data.allHeaders.length}}headers = { +{{~data.allHeaders :p:index}} '{{=p.name}}': {{=p.exampleValues.json}}{{?index < data.allHeaders.length-1}},{{?}} +{{~}}} +{{?}} +r = requests.{{=data.method.verb}}( + '{{=data.url}}', + params={{{~ data.requiredParameters :p:index}} + '{{=p.name}}': {{=p.exampleValues.json}}{{? data.requiredParameters.length-1 != index }},{{?}}{{~}}{{?data.allHeaders.length}}}, + headers = headers{{?}}) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +{{?data.allHeaders.length}}headers = { +{{~data.allHeaders :p:index}} '{{=p.name}}' => {{=p.exampleValues.json}}{{?index < data.allHeaders.length-1}},{{?}} +{{~}}}{{?}} + +result = RestClient.{{=data.method.verb}} '{{=data.url}}', + params: {{{~ data.requiredParameters :p:index}} + '{{=p.name}}' => '{{=p.safeType}}'{{? data.requiredParameters.length-1 != index }},{{?}}{{~}}}{{? data.allHeaders.length}}, headers: headers +{{?}} + +p JSON.parse(result) +``` + +
+
+
+{{?}} diff --git a/.widdershins/templates/parameters.def b/.widdershins/templates/parameters.def new file mode 100644 index 000000000..4bb8cd770 --- /dev/null +++ b/.widdershins/templates/parameters.def @@ -0,0 +1,39 @@ + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +{{~ data.parameters :p}}|{{=p.name}}|{{=p.in}}|{{=p.safeType}}|{{=p.required}}|{{=p.shortDesc || 'none'}}| +{{~}} + +{{? data.longDescs }} +##### Detailed descriptions +{{~ data.parameters :p}}{{? p.shortDesc !== p.description}} +**{{=p.name}}**: {{=p.description}}{{?}} +{{~}} +{{?}} + +{{~ data.parameters :p}} + +{{? p.schema && p.schema.enum }} +{{~ p.schema.enum :e}} +{{ var entry = {}; entry.name = p.name; entry.value = e; data.enums.push(entry); }} +{{~}} +{{?}} + +{{? p.schema && p.schema.items && p.schema.items.enum }} +{{~ p.schema.items.enum :e}} +{{ var entry = {}; entry.name = p.name; entry.value = e; data.enums.push(entry); }} +{{~}} +{{?}} + +{{~}} + +{{? data.enums && data.enums.length }} +##### Enumerated Values + +|Parameter|Value| +|---|---| +{{~ data.enums :e}}|{{=e.name}}|{{=data.utils.toPrimitive(e.value)}}| +{{~}} +{{?}} diff --git a/.widdershins/templates/responses.def b/.widdershins/templates/responses.def new file mode 100644 index 000000000..69baf3369 --- /dev/null +++ b/.widdershins/templates/responses.def @@ -0,0 +1,92 @@ +{{ data.responses = data.utils.getResponses(data); }} +{{ data.responseSchemas = false; }} +{{~ data.responses :response }} +{{ if (response.content) data.responseSchemas = true; }} +{{~}} + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +{{~ data.responses :r}}|{{=r.status}}|{{=r.meaning}}|{{=r.description || 'none'}}|{{=r.schema}}| +{{~}} + +{{ data.responseSchemas = false; }} +{{~ data.responses :response }} +{{ if (response.content && !response.$ref && !data.utils.isPrimitive(response.type)) data.responseSchemas = true; }} +{{~}} +{{? data.responseSchemas }} + +##### Response Schema +{{~ data.responses :response}} +{{? response.content && !response.$ref && !data.utils.isPrimitive(response.type)}} +{{ var responseKey = Object.keys(response.content)[0]; }} +{{ var responseSchema = response.content[responseKey].schema; }} +{{ var enums = []; }} +{{ var blocks = data.utils.schemaToArray(responseSchema,0,{trim:true,join:true},data); }} +{{ for (var block of blocks) { + for (var p of block.rows) { + if (p.schema && p.schema.enum) { + for (var e of p.schema.enum) { + enums.push({name:p.name,value:e}); + } + } + } + } +}} + +{{? blocks[0].rows.length || blocks[0].title }} +Status Code **{{=response.status}}** + +{{~ blocks :block}} +{{? block.title }}*{{=block.title}}* +{{?}} +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +{{~block.rows :p}}|{{=p.displayName}}|{{=p.safeType}}|{{=p.required}}|{{=p.restrictions||'none'}}|{{=p.description||'none'}}| +{{~}} +{{~}} +{{?}} + +{{? enums.length > 0 }} +###### Enumerated Values + +|Property|Value| +|---|---| +{{~ enums :e}}|{{=e.name}}|{{=data.utils.toPrimitive(e.value)}}| +{{~}} + +{{?}} + +{{ data.response = response; }} +{{#def.links}} + +{{?}} +{{~}} +{{?}} + +{{ data.responseHeaders = data.utils.getResponseHeaders(data); }} +{{? data.responseHeaders.length }} + +##### Response Headers + +|Status|Header|Type|Format|Description| +|---|---|---|---|---| +{{~ data.responseHeaders :h}}|{{=h.status}}|{{=h.header}}|{{=h.type}}|{{=h.format||''}}|{{=h.description||'none'}}| +{{~}} + +{{?}} + +{{ data.responseSchemas = false; }} +{{~ data.responses :response }} +{{ if (response.content) data.responseSchemas = true; }} +{{~}} + +{{? data.responseSchemas }} +##### Examples + +{{= data.utils.getResponseExamples(data) }} +{{?}} diff --git a/.widdershins/templates/security.def b/.widdershins/templates/security.def new file mode 100644 index 000000000..c266c5d6b --- /dev/null +++ b/.widdershins/templates/security.def @@ -0,0 +1,15 @@ +## Authentication + +{{ for (var s in data.api.components.securitySchemes) { }}{{ var sd = data.api.components.securitySchemes[s]; }}{{? sd.type == 'apiKey' }} +* API Key ({{=s}}) + - Parameter Name: **{{=sd.name}}**, in: {{=sd.in}}. {{=sd.description || ''}}{{?}}{{? sd.type == 'http'}} +- HTTP Authentication, scheme: {{=sd.scheme}} {{=sd.description || ''}}{{?}}{{? sd.type == 'oauth2'}} +- OAuth 2.0 Authorization. {{=sd.description || ''}}{{ for (var f in sd.flows) { }}{{ var flow = sd.flows[f]; }} + - Flow: {{=f}}{{? flow.authorizationUrl}} + - OAuth 2.0 Authorization URL = [{{=flow.authorizationUrl}}]({{=flow.authorizationUrl}}){{?}}{{? flow.tokenUrl}} + - OAuth 2.0 Token URL = [{{=flow.tokenUrl}}]({{=flow.tokenUrl}}){{?}}{{? flow.scopes}} + - OAuth 2.0 Scope + |Scope|Scope Description| + |---|---| + {{ for (var sc in flow.scopes) { }}|{{=sc}}|{{=data.utils.join(flow.scopes[sc])}}| + {{ } /* of scopes */ }}{{?}}{{ } /* of flows */ }}{{?}}{{ } /* of securitySchemes */ }} \ No newline at end of file diff --git a/.widdershins/templates/translations.dot b/.widdershins/templates/translations.dot new file mode 100644 index 000000000..fe07658ce --- /dev/null +++ b/.widdershins/templates/translations.dot @@ -0,0 +1,16 @@ +{{ +data.translations.defaultTag = 'Default'; +data.translations.response = 'Response'; +data.translations.responseDefault = 'Default'; +data.translations.responseUnknown = 'Unknown'; +data.translations.schemaInline = 'Inline'; +data.translations.schemaNone = 'None'; +data.translations.externalDocs = 'External documentation'; +data.translations.secDefNone = 'None'; +data.translations.secDefScopes = 'Scopes'; +data.translations.anonymous = 'anonymous'; +data.translations.continued = 'continued'; +data.translations.indent = '»'; +data.translations.readOnly = 'read-only'; +data.translations.writeOnly = 'write-only'; +}} diff --git a/_bal/index.js b/_bal/index.js new file mode 100644 index 000000000..62f284c8a --- /dev/null +++ b/_bal/index.js @@ -0,0 +1,215 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require('react'); + +const CompLibrary = require('../../core/CompLibrary.js'); + +const MarkdownBlock = CompLibrary.MarkdownBlock; /* Used to read markdown */ +const Container = CompLibrary.Container; +const GridBlock = CompLibrary.GridBlock; + +const siteConfig = require(`${process.cwd()}/siteConfig.js`); + +function imgUrl(img) { + return `${siteConfig.baseUrl}img/${img}`; +} + +function docUrl(doc, language) { + return `${siteConfig.baseUrl}docs/${language ? `${language}/` : ''}${doc}`; +} + +function pageUrl(page, language) { + return siteConfig.baseUrl + (language ? `${language}/` : '') + page; +} + +class Button extends React.Component { + render() { + return ( +
+ + {this.props.children} + +
+ ); + } +} + +Button.defaultProps = { + target: '_self', +}; + +const SplashContainer = props => ( +
+
+
{props.children}
+
+
+); + +const Logo = props => ( +
+ Project Logo +
+); + +const ProjectTitle = () => ( +

+ {siteConfig.title} + {siteConfig.tagline} +

+); + +const PromoSection = props => ( +
+
+
{props.children}
+
+
+); + +class HomeSplash extends React.Component { + render() { + const language = this.props.language || ''; + return ( + + +
+ + + + + + +
+
+ ); + } +} + +const Block = props => ( + + + +); + +const Features = () => ( + + {[ + { + content: 'This is the content of my feature', + image: imgUrl('docusaurus.svg'), + imageAlign: 'top', + title: 'Feature One', + }, + { + content: 'The content of my second feature', + image: imgUrl('docusaurus.svg'), + imageAlign: 'top', + title: 'Feature Two', + }, + ]} + +); + +const FeatureCallout = () => ( +
+

Feature Callout

+ These are features of this project +
+); + +const LearnHow = () => ( + + {[ + { + content: 'Talk about learning how to use this', + image: imgUrl('docusaurus.svg'), + imageAlign: 'right', + title: 'Learn How', + }, + ]} + +); + +const TryOut = () => ( + + {[ + { + content: 'Talk about trying this out', + image: imgUrl('docusaurus.svg'), + imageAlign: 'left', + title: 'Try it Out', + }, + ]} + +); + +const Description = () => ( + + {[ + { + content: 'This is another description of how this project is useful', + image: imgUrl('docusaurus.svg'), + imageAlign: 'right', + title: 'Description', + }, + ]} + +); + +const Showcase = props => { + if ((siteConfig.users || []).length === 0) { + return null; + } + + const showcase = siteConfig.users.filter(user => user.pinned).map(user => ( + + {user.caption} + + )); + + return ( +
+

Who is Using This?

+

This project is used by all these people

+
{showcase}
+
+ + More {siteConfig.title} Users + +
+
+ ); +}; + +class Index extends React.Component { + render() { + const language = this.props.language || ''; + + return ( +
+ +
+ + + + + + +
+
+ ); + } +} + +module.exports = Index; diff --git a/_bal/versions.js b/_bal/versions.js new file mode 100644 index 000000000..f08f5903d --- /dev/null +++ b/_bal/versions.js @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require('react'); + +const CompLibrary = require('../../core/CompLibrary'); + +const Container = CompLibrary.Container; + +const CWD = process.cwd(); + +const siteConfig = require(`${CWD}/siteConfig.js`); +const versions = require(`${CWD}/versions.json`); + +function Versions(props) { + const latestVersion = versions[0]; + const repoUrl = `https://github.com/${siteConfig.organizationName}/${ + siteConfig.projectName + }`; + // const language = 'en' + const language = 'en/' + return ( +
+ +
+
+

{siteConfig.title} Versions

+
+

+ Software releases and versions follow the {} + + ORY Versioning Framework + . Please read it now if you have not already, it will save you a lot of time. +

+
+ +
+

Current version (Stable)

+

Latest version.

+ + + + + + + +
{latestVersion} + + Documentation + +
+

Latest Version

+ Here you can find the latest documentation and unreleased code. + + + + + + + +
master + + Documentation + +
+

Past Versions

+

+ Here you can find documentation for previous versions. +

+ + + {versions.map( + version => + version !== latestVersion && ( + + + + + ), + )} + +
{version} + + Documentation + +
+

+ You can find past versions of this project on{' '} + GitHub. +

+
+
+
+ ); +} + +Versions.title = 'Versions'; + +module.exports = Versions; diff --git a/_legacy/_master/.circleci/config.yml b/_legacy/_master/.circleci/config.yml new file mode 100644 index 000000000..2744cc841 --- /dev/null +++ b/_legacy/_master/.circleci/config.yml @@ -0,0 +1,86 @@ +# Golang CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-go/ for more details +version: 2 +jobs: + publish: + docker: + - image: circleci/node:9-browsers + working_directory: /home/circleci/docs + environment: + - GITBOOK_DIR: /home/circleci/docs/.gitbook + - VERSION: latest + steps: + - checkout + - run: > + if [ -z ${CIRCLE_TAG+x} ]; then echo "Skipping tagging because not a tag"; else find ./guides/ -name "*.md" -type f -print0 | xargs -0 sed -i 's/'$(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1))'/'${CIRCLE_TAG}'/g'; fi + - run: yarn install + - run: yarn build + - run: git config --global user.email "hi@ory.am" + - run: git config --global user.name "ORY Continuous Integration" + - run: "git clone https://arekkas:$DOCS_TOKEN_PUSH@github.com/ory/ory-am.git ../ory-am" + + - run: "VERSION=${CIRCLE_TAG:-master}; mkdir -p ../ory-am/docs/guides/$VERSION/ || exit 0" + - run: "VERSION=${CIRCLE_TAG:-master}; mkdir -p ../ory-am/docs/apis/$VERSION/ || exit 0" + + - run: "VERSION=${CIRCLE_TAG:-master}; rm -rf ../ory-am/docs/guides/$VERSION/* || exit 0" + - run: "VERSION=${CIRCLE_TAG:-master}; rm -rf ../ory-am/docs/apis/$VERSION/* || exit 0" + + - run: "cp -R ./build/faq/* ../ory-am/docs/faq/" + + - run: "VERSION=${CIRCLE_TAG:-master}; cp -R ./build/guides/* ../ory-am/docs/guides/$VERSION/" + - run: "VERSION=${CIRCLE_TAG:-master}; cp -R ./apis/* ../ory-am/docs/apis/$VERSION/" + + - run: 'if [ -z ${CIRCLE_TAG+x} ]; then echo "Skipping push to guides latest"; else (rm -rf ../ory-am/docs/guides/latest; mkdir -p ../ory-am/docs/guides/latest; cp -R ./build/guides/* ../ory-am/docs/guides/latest); fi' + - run: 'if [ -z ${CIRCLE_TAG+x} ]; then echo "Skipping push to apis latest"; else (rm -rf ../ory-am/docs/apis/latest; mkdir -p ../ory-am/docs/apis/latest; cp -R ./apis/* ../ory-am/docs/apis/latest); fi' + + - run: "VERSION=${CIRCLE_TAG:-master}; cp -R ./build/guides/* ../ory-am/docs/guides/$VERSION/" + - run: "VERSION=${CIRCLE_TAG:-master}; cp -R ./apis/* ../ory-am/docs/apis/$VERSION/" + + - run: "(VERSION=${CIRCLE_TAG:-master}; cd ../ory-am && git add -A && git commit -a -m \"docs: Updates documentation version $VERSION\" && git push origin) || exit 0" + + tag: + docker: + - image: circleci/node:9-browsers + working_directory: /home/circleci/docs + steps: + - checkout + - run: git config --global user.email "hi@ory.am" + - run: git config --global user.name "ORY Continuous Integration" + - run: + shell: /bin/bash + command: ./.circleci/tag.sh + - run: cat ./guides/book.json + - run: cat ./guides/package.json + - run: > + find ./guides/ -name "*.md" -type f -print0 | xargs -0 sed -i 's/'$(git describe --abbrev=0 --tags $(git rev-list --tags --skip=1 --max-count=1))'/'${CIRCLE_TAG}'/g' + - run: "git commit -a -m \"Updates guides to version ${CIRCLE_TAG}\"" + - run: git remote rm origin + - run: git remote add origin https://arekkas:$GITHUB_TOKEN@github.com/ory/docs.git + - run: git push origin HEAD:master + +workflows: + version: 2 + "publish-master": + jobs: + - publish: + filters: + branches: + only: master + "publish-tag": + jobs: + - publish: + filters: + branches: + ignore: /.*/ + tags: + only: /.*/ + - tag: +# This avoids race conditions (as both publish-tag and publish-master push to the website): + requires: + - publish + filters: + branches: + ignore: /.*/ + tags: + only: /.*/ diff --git a/_legacy/_master/.circleci/tag.sh b/_legacy/_master/.circleci/tag.sh new file mode 100644 index 000000000..a45af200e --- /dev/null +++ b/_legacy/_master/.circleci/tag.sh @@ -0,0 +1,13 @@ +#/bin/bash + +cd "$( dirname "${BASH_SOURCE[0]}" )/../guides" + +contents=$(cat book.json | sed 's/[[:space:]]\+\"text\": \"latest (stable)\"/ \"text\": \"latest (stable)\"\ + },\ + {\ + \"value\": \"https:\/\/www.ory.sh\/docs\/guides\/'"${CIRCLE_TAG}"'\",\ + \"text\": \"'"${CIRCLE_TAG}"'\"/') + +echo "$contents" + +echo "$contents" > book.json diff --git a/_legacy/_master/.gitignore b/_legacy/_master/.gitignore new file mode 100644 index 000000000..ebc401b61 --- /dev/null +++ b/_legacy/_master/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +build/ +_book/ diff --git a/_legacy/_master/LICENSE b/_legacy/_master/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/_legacy/_master/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/_legacy/_master/README.md b/_legacy/_master/README.md new file mode 100644 index 000000000..19203e6f0 --- /dev/null +++ b/_legacy/_master/README.md @@ -0,0 +1,7 @@ +# ORY Documentation + +[![CircleCI](https://circleci.com/gh/ory/docs/tree/master.svg?style=shield)](https://circleci.com/gh/ory/docs/tree/master) + +This repository contains the documentation (HTTP API Reference, User Guides, FAQ) for all ORY products. Changes, when merged or pushed to master, are directly deployed to the website. + +To make changes, simply modify the markdown files. diff --git a/_legacy/_master/apis/.gitkeep b/_legacy/_master/apis/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/_legacy/_master/apis/hydra.json b/_legacy/_master/apis/hydra.json new file mode 100644 index 000000000..b4e404b30 --- /dev/null +++ b/_legacy/_master/apis/hydra.json @@ -0,0 +1,2776 @@ +{ + "consumes": [ + "application/json", + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "swagger": "2.0", + "info": { + "description": "Welcome to the ORY Hydra HTTP API documentation. You will find documentation for all HTTP APIs here. Keep in mind that this document reflects the latest branch, always. Support for versioned documentation is coming in the future.", + "title": "ORY Hydra - Cloud Native OAuth 2.0 and OpenID Connect Server", + "contact": { + "name": "ORY", + "url": "https://www.ory.sh", + "email": "hi@ory.am" + }, + "license": { + "name": "Apache 2.0", + "url": "https://github.com/ory/hydra/blob/master/LICENSE" + }, + "version": "Latest" + }, + "basePath": "/", + "paths": { + "/.well-known/jwks.json": { + "get": { + "description": "Returns metadata for discovering important JSON Web Keys. Currently, this endpoint returns the public key for verifying OpenID Connect ID Tokens.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Get Well-Known JSON Web Keys", + "operationId": "wellKnown", + "responses": { + "200": { + "description": "JSONWebKeySet", + "schema": { + "$ref": "#/definitions/JSONWebKeySet" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/.well-known/openid-configuration": { + "get": { + "description": "The well known endpoint an be used to retrieve information for OpenID Connect clients. We encourage you to not roll\nyour own OpenID Connect client but to use an OpenID Connect client library instead. You can learn more on this\nflow at https://openid.net/specs/openid-connect-discovery-1_0.html", + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Server well known configuration", + "operationId": "getWellKnown", + "responses": { + "200": { + "description": "wellKnown", + "schema": { + "$ref": "#/definitions/wellKnown" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/clients": { + "get": { + "description": "This endpoint lists all clients in the database, and never returns client secrets.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "List OAuth 2.0 Clients", + "operationId": "listOAuth2Clients", + "parameters": [ + { + "type": "integer", + "format": "int64", + "x-go-name": "Limit", + "description": "The maximum amount of policies returned.", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "x-go-name": "Offset", + "description": "The offset from where to start looking.", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/oAuth2ClientList" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "post": { + "description": "Create a new OAuth 2.0 client If you pass `client_secret` the secret will be used, otherwise a random secret will be generated. The secret will be returned in the response and you will not be able to retrieve it later on. Write the secret down and keep it somwhere safe.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Create an OAuth 2.0 client", + "operationId": "createOAuth2Client", + "parameters": [ + { + "name": "Body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/oAuth2Client" + } + } + ], + "responses": { + "200": { + "description": "oAuth2Client", + "schema": { + "$ref": "#/definitions/oAuth2Client" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/clients/{id}": { + "get": { + "description": "Get an OAUth 2.0 client by its ID. This endpoint never returns passwords.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Get an OAuth 2.0 Client.", + "operationId": "getOAuth2Client", + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "x-go-name": "ID", + "description": "The id of the OAuth 2.0 Client.", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "oAuth2Client", + "schema": { + "$ref": "#/definitions/oAuth2Client" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "put": { + "description": "Update an existing OAuth 2.0 Client. If you pass `client_secret` the secret will be updated and returned via the API. This is the only time you will be able to retrieve the client secret, so write it down and keep it safe.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Update an OAuth 2.0 Client", + "operationId": "updateOAuth2Client", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/oAuth2Client" + } + } + ], + "responses": { + "200": { + "description": "oAuth2Client", + "schema": { + "$ref": "#/definitions/oAuth2Client" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "Delete an existing OAuth 2.0 Client by its ID.\n\nOAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Deletes an OAuth 2.0 Client", + "operationId": "deleteOAuth2Client", + "parameters": [ + { + "uniqueItems": true, + "type": "string", + "x-go-name": "ID", + "description": "The id of the OAuth 2.0 Client.", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/health/alive": { + "get": { + "description": "This endpoint returns a 200 status code when the HTTP server is up running.\nThis status does currently not include checks whether the database connection is working.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Check alive status", + "operationId": "isInstanceAlive", + "responses": { + "200": { + "description": "healthStatus", + "schema": { + "$ref": "#/definitions/healthStatus" + } + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/health/ready": { + "get": { + "description": "This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g.\nthe database) are responsive as well.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.", + "produces": [ + "application/json" + ], + "tags": [ + "health" + ], + "summary": "Check readiness status", + "operationId": "isInstanceReady", + "responses": { + "200": { + "description": "healthStatus", + "schema": { + "$ref": "#/definitions/healthStatus" + } + }, + "503": { + "description": "healthNotReadyStatus", + "schema": { + "$ref": "#/definitions/healthNotReadyStatus" + } + } + } + } + }, + "/keys/{set}": { + "get": { + "description": "This endpoint can be used to retrieve JWK Sets stored in ORY Hydra.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "jsonWebKey" + ], + "summary": "Retrieve a JSON Web Key Set", + "operationId": "getJsonWebKeySet", + "parameters": [ + { + "type": "string", + "x-go-name": "Set", + "description": "The set", + "name": "set", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "JSONWebKeySet", + "schema": { + "$ref": "#/definitions/JSONWebKeySet" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "put": { + "description": "Use this method if you do not want to let Hydra generate the JWKs for you, but instead save your own.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "jsonWebKey" + ], + "summary": "Update a JSON Web Key Set", + "operationId": "updateJsonWebKeySet", + "parameters": [ + { + "type": "string", + "x-go-name": "Set", + "description": "The set", + "name": "set", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/JSONWebKeySet" + } + } + ], + "responses": { + "200": { + "description": "JSONWebKeySet", + "schema": { + "$ref": "#/definitions/JSONWebKeySet" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "post": { + "description": "This endpoint is capable of generating JSON Web Key Sets for you. There a different strategies available, such as symmetric cryptographic keys (HS256, HS512) and asymetric cryptographic keys (RS256, ECDSA). If the specified JSON Web Key Set does not exist, it will be created.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "jsonWebKey" + ], + "summary": "Generate a new JSON Web Key", + "operationId": "createJsonWebKeySet", + "parameters": [ + { + "type": "string", + "x-go-name": "Set", + "description": "The set", + "name": "set", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/jsonWebKeySetGeneratorRequest" + } + } + ], + "responses": { + "200": { + "description": "JSONWebKeySet", + "schema": { + "$ref": "#/definitions/JSONWebKeySet" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "Use this endpoint to delete a complete JSON Web Key Set and all the keys in that set.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "jsonWebKey" + ], + "summary": "Delete a JSON Web Key Set", + "operationId": "deleteJsonWebKeySet", + "parameters": [ + { + "type": "string", + "x-go-name": "Set", + "description": "The set", + "name": "set", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/keys/{set}/{kid}": { + "get": { + "description": "This endpoint can be used to retrieve JWKs stored in ORY Hydra.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "jsonWebKey" + ], + "summary": "Retrieve a JSON Web Key", + "operationId": "getJsonWebKey", + "parameters": [ + { + "type": "string", + "x-go-name": "KID", + "description": "The kid of the desired key", + "name": "kid", + "in": "path", + "required": true + }, + { + "type": "string", + "x-go-name": "Set", + "description": "The set", + "name": "set", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "JSONWebKeySet", + "schema": { + "$ref": "#/definitions/JSONWebKeySet" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "put": { + "description": "Use this method if you do not want to let Hydra generate the JWKs for you, but instead save your own.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "jsonWebKey" + ], + "summary": "Update a JSON Web Key", + "operationId": "updateJsonWebKey", + "parameters": [ + { + "type": "string", + "x-go-name": "KID", + "description": "The kid of the desired key", + "name": "kid", + "in": "path", + "required": true + }, + { + "type": "string", + "x-go-name": "Set", + "description": "The set", + "name": "set", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/JSONWebKey" + } + } + ], + "responses": { + "200": { + "description": "JSONWebKey", + "schema": { + "$ref": "#/definitions/JSONWebKey" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "Use this endpoint to delete a single JSON Web Key.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "jsonWebKey" + ], + "summary": "Delete a JSON Web Key", + "operationId": "deleteJsonWebKey", + "parameters": [ + { + "type": "string", + "x-go-name": "KID", + "description": "The kid of the desired key", + "name": "kid", + "in": "path", + "required": true + }, + { + "type": "string", + "x-go-name": "Set", + "description": "The set", + "name": "set", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth": { + "get": { + "description": "This endpoint is not documented here because you should never use your own implementation to perform OAuth2 flows.\nOAuth2 is a very popular protocol and a library for your programming language will exists.\n\nTo learn more about this flow please refer to the specification: https://tools.ietf.org/html/rfc6749", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "The OAuth 2.0 authorize endpoint", + "operationId": "oauthAuth", + "responses": { + "302": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth/requests/consent/{challenge}": { + "get": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider\nto authenticate the user and then tell ORY Hydra now about it. If the user authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the user's behalf.\n\nThe consent provider which handles this request and is a web app implemented and hosted by you. It shows a user interface which asks the user to\ngrant or deny the client access to the requested scope (\"Application my-dropbox-app wants write access to all your private files\").\n\nThe consent challenge is appended to the consent provider's URL to which the user's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells ORY Hydra if the user accepted\nor rejected the request.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Get consent request information", + "operationId": "getConsentRequest", + "parameters": [ + { + "type": "string", + "x-go-name": "Challenge", + "name": "challenge", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "consentRequest", + "schema": { + "$ref": "#/definitions/consentRequest" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "409": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth/requests/consent/{challenge}/accept": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider\nto authenticate the user and then tell ORY Hydra now about it. If the user authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the user's behalf.\n\nThe consent provider which handles this request and is a web app implemented and hosted by you. It shows a user interface which asks the user to\ngrant or deny the client access to the requested scope (\"Application my-dropbox-app wants write access to all your private files\").\n\nThe consent challenge is appended to the consent provider's URL to which the user's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells ORY Hydra if the user accepted\nor rejected the request.\n\nThis endpoint tells ORY Hydra that the user has authorized the OAuth 2.0 client to access resources on his/her behalf.\nThe consent provider includes additional information, such as session data for access and ID tokens, and if the\nconsent request should be used as basis for future requests.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Accept an consent request", + "operationId": "acceptConsentRequest", + "parameters": [ + { + "type": "string", + "x-go-name": "Challenge", + "name": "challenge", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/acceptConsentRequest" + } + } + ], + "responses": { + "200": { + "description": "completedRequest", + "schema": { + "$ref": "#/definitions/completedRequest" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth/requests/consent/{challenge}/reject": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider\nto authenticate the user and then tell ORY Hydra now about it. If the user authenticated, he/she must now be asked if\nthe OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the user's behalf.\n\nThe consent provider which handles this request and is a web app implemented and hosted by you. It shows a user interface which asks the user to\ngrant or deny the client access to the requested scope (\"Application my-dropbox-app wants write access to all your private files\").\n\nThe consent challenge is appended to the consent provider's URL to which the user's user-agent (browser) is redirected to. The consent\nprovider uses that challenge to fetch information on the OAuth2 request and then tells ORY Hydra if the user accepted\nor rejected the request.\n\nThis endpoint tells ORY Hydra that the user has not authorized the OAuth 2.0 client to access resources on his/her behalf.\nThe consent provider must include a reason why the consent was not granted.\n\nThe response contains a redirect URL which the consent provider should redirect the user-agent to.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Reject an consent request", + "operationId": "rejectConsentRequest", + "parameters": [ + { + "type": "string", + "x-go-name": "Challenge", + "name": "challenge", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/rejectRequest" + } + } + ], + "responses": { + "200": { + "description": "completedRequest", + "schema": { + "$ref": "#/definitions/completedRequest" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth/requests/login/{challenge}": { + "get": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider\n(sometimes called \"identity provider\") to authenticate the user and then tell ORY Hydra now about it. The login\nprovider is an web-app you write and host, and it must be able to authenticate (\"show the user a login screen\")\na user (in OAuth2 the proper name for user is \"resource owner\").\n\nThe authentication challenge is appended to the login provider URL to which the user's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Get an login request", + "operationId": "getLoginRequest", + "parameters": [ + { + "type": "string", + "x-go-name": "Challenge", + "name": "challenge", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "loginRequest", + "schema": { + "$ref": "#/definitions/loginRequest" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "409": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth/requests/login/{challenge}/accept": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider\n(sometimes called \"identity provider\") to authenticate the user and then tell ORY Hydra now about it. The login\nprovider is an web-app you write and host, and it must be able to authenticate (\"show the user a login screen\")\na user (in OAuth2 the proper name for user is \"resource owner\").\n\nThe authentication challenge is appended to the login provider URL to which the user's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.\n\nThis endpoint tells ORY Hydra that the user has successfully authenticated and includes additional information such as\nthe user's ID and if ORY Hydra should remember the user's user agent for future authentication attempts by setting\na cookie.\n\nThe response contains a redirect URL which the login provider should redirect the user-agent to.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Accept an login request", + "operationId": "acceptLoginRequest", + "parameters": [ + { + "type": "string", + "x-go-name": "Challenge", + "name": "challenge", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/acceptLoginRequest" + } + } + ], + "responses": { + "200": { + "description": "completedRequest", + "schema": { + "$ref": "#/definitions/completedRequest" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth/requests/login/{challenge}/reject": { + "put": { + "description": "When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider\n(sometimes called \"identity provider\") to authenticate the user and then tell ORY Hydra now about it. The login\nprovider is an web-app you write and host, and it must be able to authenticate (\"show the user a login screen\")\na user (in OAuth2 the proper name for user is \"resource owner\").\n\nThe authentication challenge is appended to the login provider URL to which the user's user-agent (browser) is redirected to. The login\nprovider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process.\n\nThis endpoint tells ORY Hydra that the user has not authenticated and includes a reason why the authentication\nwas be denied.\n\nThe response contains a redirect URL which the login provider should redirect the user-agent to.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Reject a login request", + "operationId": "rejectLoginRequest", + "parameters": [ + { + "type": "string", + "x-go-name": "Challenge", + "name": "challenge", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/rejectRequest" + } + } + ], + "responses": { + "200": { + "description": "completedRequest", + "schema": { + "$ref": "#/definitions/completedRequest" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth/sessions/consent/{user}": { + "get": { + "description": "This endpoint lists all user's granted consent sessions, including client and granted scope", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Lists all consent sessions of a user", + "operationId": "listUserConsentSessions", + "parameters": [ + { + "type": "string", + "x-go-name": "User", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/handledConsentRequestList" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "This endpoint revokes a user's granted consent sessions and invalidates all associated OAuth 2.0 Access Tokens.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Revokes all previous consent sessions of a user", + "operationId": "revokeAllUserConsentSessions", + "parameters": [ + { + "type": "string", + "x-go-name": "User", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth/sessions/consent/{user}/{client}": { + "delete": { + "description": "This endpoint revokes a user's granted consent sessions for a specific OAuth 2.0 Client and invalidates all\nassociated OAuth 2.0 Access Tokens.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Revokes consent sessions of a user for a specific OAuth 2.0 Client", + "operationId": "revokeUserClientConsentSessions", + "parameters": [ + { + "type": "string", + "x-go-name": "User", + "name": "user", + "in": "path", + "required": true + }, + { + "type": "string", + "x-go-name": "Client", + "name": "client", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth/sessions/login/revoke": { + "get": { + "description": "This endpoint deletes ths user's login session cookie and redirects the browser to the url\nlisted in `LOGOUT_REDIRECT_URL` environment variable. This endpoint does not work as an API but has to\nbe called from the user's browser.", + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Logs user out by deleting the session cookie", + "operationId": "revokeUserLoginCookie", + "responses": { + "302": { + "$ref": "#/responses/emptyResponse" + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/auth/sessions/login/{user}": { + "delete": { + "description": "This endpoint invalidates a user's authentication session. After revoking the authentication session, the user\nhas to re-authenticate at ORY Hydra. This endpoint does not invalidate any tokens.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Invalidates a user's authentication session", + "operationId": "revokeAuthenticationSession", + "parameters": [ + { + "type": "string", + "x-go-name": "User", + "name": "user", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/flush": { + "post": { + "description": "This endpoint flushes expired OAuth2 access tokens from the database. You can set a time after which no tokens will be\nnot be touched, in case you want to keep recent tokens for auditing. Refresh tokens can not be flushed as they are deleted\nautomatically when performing the refresh flow.", + "consumes": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Flush Expired OAuth2 Access Tokens", + "operationId": "flushInactiveOAuth2Tokens", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/flushInactiveOAuth2TokensRequest" + } + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/introspect": { + "post": { + "security": [ + { + "basic": [] + }, + { + "oauth2": [] + } + ], + "description": "The introspection endpoint allows to check if a token (both refresh and access) is active or not. An active token\nis neither expired nor revoked. If a token is active, additional information on the token will be included. You can\nset additional data for a token by setting `accessTokenExtra` during the consent flow.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Introspect OAuth2 tokens", + "operationId": "introspectOAuth2Token", + "parameters": [ + { + "type": "string", + "x-go-name": "Token", + "description": "The string value of the token. For access tokens, this\nis the \"access_token\" value returned from the token endpoint\ndefined in OAuth 2.0 [RFC6749], Section 5.1.\nThis endpoint DOES NOT accept refresh tokens for validation.", + "name": "token", + "in": "formData", + "required": true + }, + { + "type": "string", + "x-go-name": "Scope", + "description": "An optional, space separated list of required scopes. If the access token was not granted one of the\nscopes, the result of active will be false.", + "name": "scope", + "in": "formData" + } + ], + "responses": { + "200": { + "description": "oAuth2TokenIntrospection", + "schema": { + "$ref": "#/definitions/oAuth2TokenIntrospection" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/revoke": { + "post": { + "security": [ + { + "basic": [] + }, + { + "oauth2": [] + } + ], + "description": "Revoking a token (both access and refresh) means that the tokens will be invalid. A revoked access token can no\nlonger be used to make access requests, and a revoked refresh token can no longer be used to refresh an access token.\nRevoking a refresh token also invalidates the access token that was created with it.", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "Revoke OAuth2 tokens", + "operationId": "revokeOAuth2Token", + "parameters": [ + { + "type": "string", + "x-go-name": "Token", + "name": "token", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/oauth2/token": { + "post": { + "security": [ + { + "basic": [] + }, + { + "oauth2": [] + } + ], + "description": "This endpoint is not documented here because you should never use your own implementation to perform OAuth2 flows.\nOAuth2 is a very popular protocol and a library for your programming language will exists.\n\nTo learn more about this flow please refer to the specification: https://tools.ietf.org/html/rfc6749", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "The OAuth 2.0 token endpoint", + "operationId": "oauthToken", + "responses": { + "200": { + "description": "oauthTokenResponse", + "schema": { + "$ref": "#/definitions/oauthTokenResponse" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/userinfo": { + "get": { + "security": [ + { + "oauth2": [] + } + ], + "description": "This endpoint returns the payload of the ID Token, including the idTokenExtra values, of the provided OAuth 2.0 access token.\nThe endpoint implements http://openid.net/specs/openid-connect-core-1_0.html#UserInfo .", + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "oAuth2" + ], + "summary": "OpenID Connect Userinfo", + "operationId": "userinfo", + "responses": { + "200": { + "description": "userinfoResponse", + "schema": { + "$ref": "#/definitions/userinfoResponse" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/version": { + "get": { + "description": "This endpoint returns the service version typically notated using semantic versioning.\n\nIf the service supports TLS Edge Termination, this endpoint does not require the\n`X-Forwarded-Proto` header to be set.\n\nBe aware that if you are running multiple nodes of this service, the health status will never\nrefer to the cluster state, only to a single instance.", + "produces": [ + "application/json" + ], + "tags": [ + "version" + ], + "summary": "Get service version", + "operationId": "getVersion", + "responses": { + "200": { + "description": "version", + "schema": { + "$ref": "#/definitions/version" + } + } + } + } + } + }, + "definitions": { + "AuthenticationSession": { + "type": "object", + "properties": { + "AuthenticatedAt": { + "type": "string", + "format": "date-time" + }, + "ID": { + "type": "string" + }, + "Subject": { + "type": "string" + } + }, + "x-go-package": "github.com/ory/hydra/consent" + }, + "JSONWebKey": { + "type": "object", + "properties": { + "alg": { + "description": "The \"alg\" (algorithm) parameter identifies the algorithm intended for\nuse with the key. The values used should either be registered in the\nIANA \"JSON Web Signature and Encryption Algorithms\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name.", + "type": "string", + "x-go-name": "Alg" + }, + "crv": { + "type": "string", + "x-go-name": "Crv" + }, + "d": { + "type": "string", + "x-go-name": "D" + }, + "dp": { + "type": "string", + "x-go-name": "Dp" + }, + "dq": { + "type": "string", + "x-go-name": "Dq" + }, + "e": { + "type": "string", + "x-go-name": "E" + }, + "k": { + "type": "string", + "x-go-name": "K" + }, + "kid": { + "description": "The \"kid\" (key ID) parameter is used to match a specific key. This\nis used, for instance, to choose among a set of keys within a JWK Set\nduring key rollover. The structure of the \"kid\" value is\nunspecified. When \"kid\" values are used within a JWK Set, different\nkeys within the JWK Set SHOULD use distinct \"kid\" values. (One\nexample in which different keys might use the same \"kid\" value is if\nthey have different \"kty\" (key type) values but are considered to be\nequivalent alternatives by the application using them.) The \"kid\"\nvalue is a case-sensitive string.", + "type": "string", + "x-go-name": "Kid" + }, + "kty": { + "description": "The \"kty\" (key type) parameter identifies the cryptographic algorithm\nfamily used with the key, such as \"RSA\" or \"EC\". \"kty\" values should\neither be registered in the IANA \"JSON Web Key Types\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name. The \"kty\" value is a case-sensitive string.", + "type": "string", + "x-go-name": "Kty" + }, + "n": { + "type": "string", + "x-go-name": "N" + }, + "p": { + "type": "string", + "x-go-name": "P" + }, + "q": { + "type": "string", + "x-go-name": "Q" + }, + "qi": { + "type": "string", + "x-go-name": "Qi" + }, + "use": { + "description": "The \"use\" (public key use) parameter identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Values are commonly \"sig\" (signature) or \"enc\" (encryption).", + "type": "string", + "x-go-name": "Use" + }, + "x": { + "type": "string", + "x-go-name": "X" + }, + "x5c": { + "description": "The \"x5c\" (X.509 certificate chain) parameter contains a chain of one\nor more PKIX certificates [RFC5280]. The certificate chain is\nrepresented as a JSON array of certificate value strings. Each\nstring in the array is a base64-encoded (Section 4 of [RFC4648] --\nnot base64url-encoded) DER [ITU.X690.1994] PKIX certificate value.\nThe PKIX certificate containing the key value MUST be the first\ncertificate.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "X5c" + }, + "y": { + "type": "string", + "x-go-name": "Y" + } + }, + "x-go-name": "swaggerJSONWebKey", + "x-go-package": "github.com/ory/hydra/jwk" + }, + "JSONWebKeySet": { + "type": "object", + "properties": { + "keys": { + "description": "The value of the \"keys\" parameter is an array of JWK values. By\ndefault, the order of the JWK values within the array does not imply\nan order of preference among them, although applications of JWK Sets\ncan choose to assign a meaning to the order for their purposes, if\ndesired.", + "type": "array", + "items": { + "$ref": "#/definitions/JSONWebKey" + }, + "x-go-name": "Keys" + } + }, + "x-go-name": "swaggerJSONWebKeySet", + "x-go-package": "github.com/ory/hydra/jwk" + }, + "PreviousConsentSession": { + "description": "The response used to return handled consent requests\nsame as HandledAuthenticationRequest, just with consent_request exposed as json", + "type": "object", + "properties": { + "consent_request": { + "$ref": "#/definitions/consentRequest" + }, + "grant_access_token_audience": { + "description": "GrantedAudience sets the audience the user authorized the client to use. Should be a subset of `requested_access_token_audience`.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "GrantedAudience" + }, + "grant_scope": { + "description": "GrantScope sets the scope the user authorized the client to use. Should be a subset of `requested_scope`", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "GrantedScope" + }, + "remember": { + "description": "Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same\nclient asks the same user for the same, or a subset of, scope.", + "type": "boolean", + "x-go-name": "Remember" + }, + "remember_for": { + "description": "RememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered indefinitely.", + "type": "integer", + "format": "int64", + "x-go-name": "RememberFor" + }, + "session": { + "$ref": "#/definitions/consentRequestSession" + } + }, + "x-go-package": "github.com/ory/hydra/consent" + }, + "RawMessage": { + "description": "It implements Marshaler and Unmarshaler and can\nbe used to delay JSON decoding or precompute a JSON encoding.", + "type": "array", + "title": "RawMessage is a raw encoded JSON value.", + "items": { + "type": "integer", + "format": "uint8" + }, + "x-go-package": "encoding/json" + }, + "acceptConsentRequest": { + "type": "object", + "title": "The request payload used to accept a consent request.", + "properties": { + "grant_access_token_audience": { + "description": "GrantedAudience sets the audience the user authorized the client to use. Should be a subset of `requested_access_token_audience`.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "GrantedAudience" + }, + "grant_scope": { + "description": "GrantScope sets the scope the user authorized the client to use. Should be a subset of `requested_scope`.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "GrantedScope" + }, + "remember": { + "description": "Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same\nclient asks the same user for the same, or a subset of, scope.", + "type": "boolean", + "x-go-name": "Remember" + }, + "remember_for": { + "description": "RememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered indefinitely.", + "type": "integer", + "format": "int64", + "x-go-name": "RememberFor" + }, + "session": { + "$ref": "#/definitions/consentRequestSession" + } + }, + "x-go-name": "HandledConsentRequest", + "x-go-package": "github.com/ory/hydra/consent" + }, + "acceptLoginRequest": { + "type": "object", + "title": "The request payload used to accept a login request.", + "properties": { + "acr": { + "description": "ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it\nto express that, for example, a user authenticated using two factor authentication.", + "type": "string", + "x-go-name": "ACR" + }, + "force_subject_identifier": { + "description": "ForceSubjectIdentifier forces the \"pairwise\" user ID of the end-user that authenticated. The \"pairwise\" user ID refers to the\n(Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID\nConnect specification. It allows you to set an obfuscated subject (\"user\") identifier that is unique to the client.\n\nPlease note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the\nsub claim in the OAuth 2.0 Introspection.\n\nPer default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself\nyou can use this field. Please note that setting this field has no effect if `pairwise` is not configured in\nORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's\nconfiguration).\n\nPlease also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies\nthat you have to compute this value on every authentication process (probably depending on the client ID or some\nother unique value).\n\nIf you fail to compute the proper value, then authentication processes which have id_token_hint set might fail.", + "type": "string", + "x-go-name": "ForceSubjectIdentifier" + }, + "remember": { + "description": "Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store\na cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she\nwill not be asked to log in again.", + "type": "boolean", + "x-go-name": "Remember" + }, + "remember_for": { + "description": "RememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the\nauthorization will be remembered indefinitely.", + "type": "integer", + "format": "int64", + "x-go-name": "RememberFor" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "HandledAuthenticationRequest", + "x-go-package": "github.com/ory/hydra/consent" + }, + "completedRequest": { + "type": "object", + "title": "The response payload sent when accepting or rejecting a login or consent request.", + "properties": { + "redirect_to": { + "description": "RedirectURL is the URL which you should redirect the user to once the authentication process is completed.", + "type": "string", + "x-go-name": "RedirectTo" + } + }, + "x-go-name": "RequestHandlerResponse", + "x-go-package": "github.com/ory/hydra/consent" + }, + "consentRequest": { + "type": "object", + "title": "Contains information on an ongoing consent request.", + "properties": { + "challenge": { + "description": "Challenge is the identifier (\"authorization challenge\") of the consent authorization request. It is used to\nidentify the session.", + "type": "string", + "x-go-name": "Challenge" + }, + "client": { + "$ref": "#/definitions/oAuth2Client" + }, + "login_challenge": { + "description": "LoginChallenge is the login challenge this consent challenge belongs to. It can be used to associate\na login and consent request in the login \u0026 consent app.", + "type": "string", + "x-go-name": "LoginChallenge" + }, + "login_session_id": { + "description": "LoginSessionID is the authentication session ID. It is set if the browser had a valid authentication session at\nORY Hydra during the login flow. It can be used to associate consecutive login requests by a certain user.", + "type": "string", + "x-go-name": "LoginSessionID" + }, + "oidc_context": { + "$ref": "#/definitions/openIDConnectContext" + }, + "request_url": { + "description": "RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which\ninitiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but\nmight come in handy if you want to deal with additional request parameters.", + "type": "string", + "x-go-name": "RequestURL" + }, + "requested_access_token_audience": { + "description": "RequestedScope contains the access token audience as requested by the OAuth 2.0 Client.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "RequestedAudience" + }, + "requested_scope": { + "description": "RequestedScope contains the OAuth 2.0 Scope requested by the OAuth 2.0 Client.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "RequestedScope" + }, + "skip": { + "description": "Skip, if true, implies that the client has requested the same scopes from the same user previously.\nIf true, you must not ask the user to grant the requested scopes. You must however either allow or deny the\nconsent request using the usual API call.", + "type": "boolean", + "x-go-name": "Skip" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope\nrequested by the OAuth 2.0 client.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "ConsentRequest", + "x-go-package": "github.com/ory/hydra/consent" + }, + "consentRequestSession": { + "type": "object", + "title": "Used to pass session data to a consent request.", + "properties": { + "access_token": { + "description": "AccessToken sets session data for the access and refresh token, as well as any future tokens issued by the\nrefresh grant. Keep in mind that this data will be available to anyone performing OAuth 2.0 Challenge Introspection.\nIf only your services can perform OAuth 2.0 Challenge Introspection, this is usually fine. But if third parties\ncan access that endpoint as well, sensitive data from the session might be exposed to them. Use with care!", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "AccessToken" + }, + "id_token": { + "description": "IDToken sets session data for the OpenID Connect ID token. Keep in mind that the session'id payloads are readable\nby anyone that has access to the ID Challenge. Use with care!", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "IDToken" + } + }, + "x-go-name": "ConsentRequestSessionData", + "x-go-package": "github.com/ory/hydra/consent" + }, + "flushInactiveOAuth2TokensRequest": { + "type": "object", + "properties": { + "notAfter": { + "description": "NotAfter sets after which point tokens should not be flushed. This is useful when you want to keep a history\nof recently issued tokens for auditing.", + "type": "string", + "format": "date-time", + "x-go-name": "NotAfter" + } + }, + "x-go-name": "FlushInactiveOAuth2TokensRequest", + "x-go-package": "github.com/ory/hydra/oauth2" + }, + "healthNotReadyStatus": { + "type": "object", + "properties": { + "errors": { + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "x-go-name": "Errors" + } + }, + "x-go-name": "swaggerNotReadyStatus", + "x-go-package": "github.com/ory/x/healthx" + }, + "healthStatus": { + "type": "object", + "properties": { + "status": { + "description": "Status always contains \"ok\".", + "type": "string", + "x-go-name": "Status" + } + }, + "x-go-name": "swaggerHealthStatus", + "x-go-package": "github.com/ory/x/healthx" + }, + "joseWebKeySetRequest": { + "type": "object", + "properties": { + "keys": { + "type": "array", + "items": { + "$ref": "#/definitions/RawMessage" + }, + "x-go-name": "Keys" + } + }, + "x-go-package": "github.com/ory/hydra/jwk" + }, + "jsonWebKeySetGeneratorRequest": { + "type": "object", + "required": [ + "alg", + "use", + "kid" + ], + "properties": { + "alg": { + "description": "The algorithm to be used for creating the key. Supports \"RS256\", \"ES512\", \"HS512\", and \"HS256\"", + "type": "string", + "x-go-name": "Algorithm" + }, + "kid": { + "description": "The kid of the key to be created", + "type": "string", + "x-go-name": "KeyID" + }, + "use": { + "description": "The \"use\" (public key use) parameter identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Valid values are \"enc\" and \"sig\".", + "type": "string", + "x-go-name": "Use" + } + }, + "x-go-name": "createRequest", + "x-go-package": "github.com/ory/hydra/jwk" + }, + "loginRequest": { + "type": "object", + "title": "Contains information on an ongoing login request.", + "properties": { + "challenge": { + "description": "Challenge is the identifier (\"authentication challenge\") of the consent authentication request. It is used to\nidentify the session.", + "type": "string", + "x-go-name": "Challenge" + }, + "client": { + "$ref": "#/definitions/oAuth2Client" + }, + "oidc_context": { + "$ref": "#/definitions/openIDConnectContext" + }, + "request_url": { + "description": "RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which\ninitiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but\nmight come in handy if you want to deal with additional request parameters.", + "type": "string", + "x-go-name": "RequestURL" + }, + "requested_access_token_audience": { + "description": "RequestedScope contains the access token audience as requested by the OAuth 2.0 Client.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "RequestedAudience" + }, + "requested_scope": { + "description": "RequestedScope contains the OAuth 2.0 Scope requested by the OAuth 2.0 Client.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "RequestedScope" + }, + "session_id": { + "description": "SessionID is the authentication session ID. It is set if the browser had a valid authentication session at\nORY Hydra during the login flow. It can be used to associate consecutive login requests by a certain user.", + "type": "string", + "x-go-name": "SessionID" + }, + "skip": { + "description": "Skip, if true, implies that the client has requested the same scopes from the same user previously.\nIf true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL.\n\nThis feature allows you to update / set session information.", + "type": "boolean", + "x-go-name": "Skip" + }, + "subject": { + "description": "Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope\nrequested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type\nwhen accepting the login request, or the request will fail.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "AuthenticationRequest", + "x-go-package": "github.com/ory/hydra/consent" + }, + "oAuth2Client": { + "type": "object", + "title": "Client represents an OAuth 2.0 Client.", + "properties": { + "allowed_cors_origins": { + "description": "AllowedCORSOrigins are one or more URLs (scheme://host[:port]) which are allowed to make CORS requests\nto the /oauth/token endpoint. If this array is empty, the sever's CORS origin configuration (`CORS_ALLOWED_ORIGINS`)\nwill be used instead. If this array is set, the allowed origins are appended to the server's CORS origin configuration.\nBe aware that environment variable `CORS_ENABLED` MUST be set to `true` for this to work.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "AllowedCORSOrigins" + }, + "audience": { + "description": "Audience is a whitelist defining the audiences this client is allowed to request tokens for. An audience limits\nthe applicability of an OAuth 2.0 Access Token to, for example, certain API endpoints. The value is a list\nof URLs. URLs MUST NOT contain whitespaces.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Audience" + }, + "client_id": { + "description": "ClientID is the id for this client.", + "type": "string", + "x-go-name": "ClientID" + }, + "client_name": { + "description": "Name is the human-readable string name of the client to be presented to the\nend-user during authorization.", + "type": "string", + "x-go-name": "Name" + }, + "client_secret": { + "description": "Secret is the client's secret. The secret will be included in the create request as cleartext, and then\nnever again. The secret is stored using BCrypt so it is impossible to recover it. Tell your users\nthat they need to write the secret down as it will not be made available again.", + "type": "string", + "x-go-name": "Secret" + }, + "client_secret_expires_at": { + "description": "SecretExpiresAt is an integer holding the time at which the client\nsecret will expire or 0 if it will not expire. The time is\nrepresented as the number of seconds from 1970-01-01T00:00:00Z as\nmeasured in UTC until the date/time of expiration.", + "type": "integer", + "format": "int64", + "x-go-name": "SecretExpiresAt" + }, + "client_uri": { + "description": "ClientURI is an URL string of a web page providing information about the client.\nIf present, the server SHOULD display this URL to the end-user in\na clickable fashion.", + "type": "string", + "x-go-name": "ClientURI" + }, + "contacts": { + "description": "Contacts is a array of strings representing ways to contact people responsible\nfor this client, typically email addresses.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Contacts" + }, + "grant_types": { + "description": "GrantTypes is an array of grant types the client is allowed to use.", + "type": "array", + "pattern": "client_credentials|authorize_code|implicit|refresh_token", + "items": { + "type": "string" + }, + "x-go-name": "GrantTypes" + }, + "jwks": { + "$ref": "#/definitions/JSONWebKeySet" + }, + "jwks_uri": { + "description": "URL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains\nthe signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the\nClient's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing\nand encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced\nJWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both\nsignatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used\nto provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST\nmatch those in the certificate.", + "type": "string", + "x-go-name": "JSONWebKeysURI" + }, + "logo_uri": { + "description": "LogoURI is an URL string that references a logo for the client.", + "type": "string", + "x-go-name": "LogoURI" + }, + "owner": { + "description": "Owner is a string identifying the owner of the OAuth 2.0 Client.", + "type": "string", + "x-go-name": "Owner" + }, + "policy_uri": { + "description": "PolicyURI is a URL string that points to a human-readable privacy policy document\nthat describes how the deployment organization collects, uses,\nretains, and discloses personal data.", + "type": "string", + "x-go-name": "PolicyURI" + }, + "redirect_uris": { + "description": "RedirectURIs is an array of allowed redirect urls for the client, for example http://mydomain/oauth/callback .", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "RedirectURIs" + }, + "request_object_signing_alg": { + "description": "JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects\nfrom this Client MUST be rejected, if not signed with this algorithm.", + "type": "string", + "x-go-name": "RequestObjectSigningAlgorithm" + }, + "request_uris": { + "description": "Array of request_uri values that are pre-registered by the RP for use at the OP. Servers MAY cache the\ncontents of the files referenced by these URIs and not retrieve them at the time they are used in a request.\nOPs can require that request_uri values used be pre-registered with the require_request_uri_registration\ndiscovery parameter.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "RequestURIs" + }, + "response_types": { + "description": "ResponseTypes is an array of the OAuth 2.0 response type strings that the client can\nuse at the authorization endpoint.", + "type": "array", + "pattern": "id_token|code|token", + "items": { + "type": "string" + }, + "x-go-name": "ResponseTypes" + }, + "scope": { + "description": "Scope is a string containing a space-separated list of scope values (as\ndescribed in Section 3.3 of OAuth 2.0 [RFC6749]) that the client\ncan use when requesting access tokens.", + "type": "string", + "pattern": "([a-zA-Z0-9\\.\\*]+\\s?)+", + "x-go-name": "Scope" + }, + "sector_identifier_uri": { + "description": "URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a\nfile with a single JSON array of redirect_uri values.", + "type": "string", + "x-go-name": "SectorIdentifierURI" + }, + "subject_type": { + "description": "SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a\nlist of the supported subject_type values for this server. Valid types include `pairwise` and `public`.", + "type": "string", + "x-go-name": "SubjectType" + }, + "token_endpoint_auth_method": { + "description": "Requested Client Authentication method for the Token Endpoint. The options are client_secret_post,\nclient_secret_basic, private_key_jwt, and none.", + "type": "string", + "x-go-name": "TokenEndpointAuthMethod" + }, + "tos_uri": { + "description": "TermsOfServiceURI is a URL string that points to a human-readable terms of service\ndocument for the client that describes a contractual relationship\nbetween the end-user and the client that the end-user accepts when\nauthorizing the client.", + "type": "string", + "x-go-name": "TermsOfServiceURI" + }, + "userinfo_signed_response_alg": { + "description": "JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT\n[JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims\nas a UTF-8 encoded JSON object using the application/json content-type.", + "type": "string", + "x-go-name": "UserinfoSignedResponseAlg" + } + }, + "x-go-name": "Client", + "x-go-package": "github.com/ory/hydra/client" + }, + "oAuth2TokenIntrospection": { + "description": "https://tools.ietf.org/html/rfc7662", + "type": "object", + "title": "Introspection contains an access token's session data as specified by IETF RFC 7662, see:", + "required": [ + "active" + ], + "properties": { + "active": { + "description": "Active is a boolean indicator of whether or not the presented token\nis currently active. The specifics of a token's \"active\" state\nwill vary depending on the implementation of the authorization\nserver and the information it keeps about its tokens, but a \"true\"\nvalue return for the \"active\" property will generally indicate\nthat a given token has been issued by this authorization server,\nhas not been revoked by the resource owner, and is within its\ngiven time window of validity (e.g., after its issuance time and\nbefore its expiration time).", + "type": "boolean", + "x-go-name": "Active" + }, + "aud": { + "description": "Audience contains a list of the token's intended audiences.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Audience" + }, + "client_id": { + "description": "ClientID is aclient identifier for the OAuth 2.0 client that\nrequested this token.", + "type": "string", + "x-go-name": "ClientID" + }, + "exp": { + "description": "Expires at is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token will expire.", + "type": "integer", + "format": "int64", + "x-go-name": "ExpiresAt" + }, + "ext": { + "description": "Extra is arbitrary data set by the session.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Extra" + }, + "iat": { + "description": "Issued at is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token was\noriginally issued.", + "type": "integer", + "format": "int64", + "x-go-name": "IssuedAt" + }, + "iss": { + "description": "IssuerURL is a string representing the issuer of this token", + "type": "string", + "x-go-name": "Issuer" + }, + "nbf": { + "description": "NotBefore is an integer timestamp, measured in the number of seconds\nsince January 1 1970 UTC, indicating when this token is not to be\nused before.", + "type": "integer", + "format": "int64", + "x-go-name": "NotBefore" + }, + "obfuscated_subject": { + "description": "ObfuscatedSubject is set when the subject identifier algorithm was set to \"pairwise\" during authorization.\nIt is the `sub` value of the ID Token that was issued.", + "type": "string", + "x-go-name": "ObfuscatedSubject" + }, + "scope": { + "description": "Scope is a JSON string containing a space-separated list of\nscopes associated with this token.", + "type": "string", + "x-go-name": "Scope" + }, + "sub": { + "description": "Subject of the token, as defined in JWT [RFC7519].\nUsually a machine-readable identifier of the resource owner who\nauthorized this token.", + "type": "string", + "x-go-name": "Subject" + }, + "token_type": { + "description": "TokenType is the introspected token's type, for example `access_token` or `refresh_token`.", + "type": "string", + "x-go-name": "TokenType" + }, + "username": { + "description": "Username is a human-readable identifier for the resource owner who\nauthorized this token.", + "type": "string", + "x-go-name": "Username" + } + }, + "x-go-name": "Introspection", + "x-go-package": "github.com/ory/hydra/oauth2" + }, + "oauthTokenResponse": { + "description": "The token response", + "type": "object", + "properties": { + "access_token": { + "description": "The access token issued by the authorization server.", + "type": "string", + "x-go-name": "AccessToken" + }, + "expires_in": { + "description": "The lifetime in seconds of the access token. For\nexample, the value \"3600\" denotes that the access token will\nexpire in one hour from the time the response was generated.", + "type": "integer", + "format": "int64", + "x-go-name": "ExpiresIn" + }, + "id_token": { + "description": "To retrieve a refresh token request the id_token scope.", + "type": "integer", + "format": "int64", + "x-go-name": "IDToken" + }, + "refresh_token": { + "description": "The refresh token, which can be used to obtain new\naccess tokens. To retrieve it add the scope \"offline\" to your access token request.", + "type": "string", + "x-go-name": "RefreshToken" + }, + "scope": { + "description": "The scope of the access token", + "type": "integer", + "format": "int64", + "x-go-name": "Scope" + }, + "token_type": { + "description": "The type of the token issued", + "type": "string", + "x-go-name": "TokenType" + } + }, + "x-go-name": "swaggerOAuthTokenResponse", + "x-go-package": "github.com/ory/hydra/oauth2" + }, + "openIDConnectContext": { + "type": "object", + "title": "Contains optional information about the OpenID Connect request.", + "properties": { + "acr_values": { + "description": "ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request.\nIt is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required.\n\nOpenID Connect defines it as follows:\n\u003e Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values\nthat the Authorization Server is being requested to use for processing this Authentication Request, with the\nvalues appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication\nperformed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a\nVoluntary Claim by this parameter.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "ACRValues" + }, + "display": { + "description": "Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User.\nThe defined values are:\npage: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode.\npopup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over.\ntouch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface.\nwap: The Authorization Server SHOULD display the authentication and consent UI consistent with a \"feature phone\" type display.\n\nThe Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display.", + "type": "string", + "x-go-name": "Display" + }, + "id_token_hint_claims": { + "description": "IDTokenHintClaims are the claims of the ID Token previously issued by the Authorization Server being passed as a hint about the\nEnd-User's current or past authenticated session with the Client.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "IDTokenHintClaims" + }, + "login_hint": { + "description": "LoginHint hints about the login identifier the End-User might use to log in (if necessary).\nThis hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier)\nand then wants to pass that value as a hint to the discovered authorization service. This value MAY also be a\nphone number in the format specified for the phone_number Claim. The use of this parameter is optional.", + "type": "string", + "x-go-name": "LoginHint" + }, + "ui_locales": { + "description": "UILocales is the End-User'id preferred languages and scripts for the user interface, represented as a\nspace-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value\n\"fr-CA fr en\" represents a preference for French as spoken in Canada, then French (without a region designation),\nfollowed by English (without a region designation). An error SHOULD NOT result if some or all of the requested\nlocales are not supported by the OpenID Provider.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "UILocales" + } + }, + "x-go-name": "OpenIDConnectContext", + "x-go-package": "github.com/ory/hydra/consent" + }, + "rejectRequest": { + "type": "object", + "title": "The request payload used to accept a login or consent request.", + "properties": { + "error": { + "type": "string", + "x-go-name": "Name" + }, + "error_debug": { + "type": "string", + "x-go-name": "Debug" + }, + "error_description": { + "type": "string", + "x-go-name": "Description" + }, + "error_hint": { + "type": "string", + "x-go-name": "Hint" + }, + "status_code": { + "type": "integer", + "format": "int64", + "x-go-name": "Code" + } + }, + "x-go-name": "RequestDeniedError", + "x-go-package": "github.com/ory/hydra/consent" + }, + "swaggerFlushInactiveAccessTokens": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/flushInactiveOAuth2TokensRequest" + } + }, + "x-go-package": "github.com/ory/hydra/oauth2" + }, + "swaggerJsonWebKeyQuery": { + "type": "object", + "required": [ + "kid", + "set" + ], + "properties": { + "kid": { + "description": "The kid of the desired key\nin: path", + "type": "string", + "x-go-name": "KID" + }, + "set": { + "description": "The set\nin: path", + "type": "string", + "x-go-name": "Set" + } + }, + "x-go-package": "github.com/ory/hydra/jwk" + }, + "swaggerJwkCreateSet": { + "type": "object", + "required": [ + "set" + ], + "properties": { + "Body": { + "$ref": "#/definitions/jsonWebKeySetGeneratorRequest" + }, + "set": { + "description": "The set\nin: path", + "type": "string", + "x-go-name": "Set" + } + }, + "x-go-package": "github.com/ory/hydra/jwk" + }, + "swaggerJwkSetQuery": { + "type": "object", + "required": [ + "set" + ], + "properties": { + "set": { + "description": "The set\nin: path", + "type": "string", + "x-go-name": "Set" + } + }, + "x-go-package": "github.com/ory/hydra/jwk" + }, + "swaggerJwkUpdateSet": { + "type": "object", + "required": [ + "set" + ], + "properties": { + "Body": { + "$ref": "#/definitions/JSONWebKeySet" + }, + "set": { + "description": "The set\nin: path", + "type": "string", + "x-go-name": "Set" + } + }, + "x-go-package": "github.com/ory/hydra/jwk" + }, + "swaggerJwkUpdateSetKey": { + "type": "object", + "required": [ + "kid", + "set" + ], + "properties": { + "Body": { + "$ref": "#/definitions/JSONWebKey" + }, + "kid": { + "description": "The kid of the desired key\nin: path", + "type": "string", + "x-go-name": "KID" + }, + "set": { + "description": "The set\nin: path", + "type": "string", + "x-go-name": "Set" + } + }, + "x-go-package": "github.com/ory/hydra/jwk" + }, + "swaggerOAuthIntrospectionRequest": { + "type": "object", + "required": [ + "token" + ], + "properties": { + "scope": { + "description": "An optional, space separated list of required scopes. If the access token was not granted one of the\nscopes, the result of active will be false.\n\nin: formData", + "type": "string", + "x-go-name": "Scope" + }, + "token": { + "description": "The string value of the token. For access tokens, this\nis the \"access_token\" value returned from the token endpoint\ndefined in OAuth 2.0 [RFC6749], Section 5.1.\nThis endpoint DOES NOT accept refresh tokens for validation.", + "type": "string", + "x-go-name": "Token" + } + }, + "x-go-package": "github.com/ory/hydra/oauth2" + }, + "swaggerRevokeOAuth2TokenParameters": { + "type": "object", + "required": [ + "token" + ], + "properties": { + "token": { + "description": "in: formData", + "type": "string", + "x-go-name": "Token" + } + }, + "x-go-package": "github.com/ory/hydra/oauth2" + }, + "userinfoResponse": { + "description": "The userinfo response", + "type": "object", + "properties": { + "birthdate": { + "description": "End-User's birthday, represented as an ISO 8601:2004 [ISO8601‑2004] YYYY-MM-DD format. The year MAY be 0000, indicating that it is omitted. To represent only the year, YYYY format is allowed. Note that depending on the underlying platform's date related function, providing just year can result in varying month and day, so the implementers need to take this factor into account to correctly process the dates.", + "type": "string", + "x-go-name": "Birthdate" + }, + "email": { + "description": "End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 [RFC5322] addr-spec syntax. The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.", + "type": "string", + "x-go-name": "Email" + }, + "email_verified": { + "description": "True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.", + "type": "boolean", + "x-go-name": "EmailVerified" + }, + "family_name": { + "description": "Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.", + "type": "string", + "x-go-name": "FamilyName" + }, + "gender": { + "description": "End-User's gender. Values defined by this specification are female and male. Other values MAY be used when neither of the defined values are applicable.", + "type": "string", + "x-go-name": "Gender" + }, + "given_name": { + "description": "Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.", + "type": "string", + "x-go-name": "GivenName" + }, + "locale": { + "description": "End-User's locale, represented as a BCP47 [RFC5646] language tag. This is typically an ISO 639-1 Alpha-2 [ISO639‑1] language code in lowercase and an ISO 3166-1 Alpha-2 [ISO3166‑1] country code in uppercase, separated by a dash. For example, en-US or fr-CA. As a compatibility note, some implementations have used an underscore as the separator rather than a dash, for example, en_US; Relying Parties MAY choose to accept this locale syntax as well.", + "type": "string", + "x-go-name": "Locale" + }, + "middle_name": { + "description": "Middle name(s) of the End-User. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used.", + "type": "string", + "x-go-name": "MiddleName" + }, + "name": { + "description": "End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences.", + "type": "string", + "x-go-name": "Name" + }, + "nickname": { + "description": "Casual name of the End-User that may or may not be the same as the given_name. For instance, a nickname value of Mike might be returned alongside a given_name value of Michael.", + "type": "string", + "x-go-name": "Nickname" + }, + "phone_number": { + "description": "End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED as the format of this Claim, for example, +1 (425) 555-1212 or +56 (2) 687 2400. If the phone number contains an extension, it is RECOMMENDED that the extension be represented using the RFC 3966 [RFC3966] extension syntax, for example, +1 (604) 555-1234;ext=5678.", + "type": "string", + "x-go-name": "PhoneNumber" + }, + "phone_number_verified": { + "description": "True if the End-User's phone number has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this phone number was controlled by the End-User at the time the verification was performed. The means by which a phone number is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating. When true, the phone_number Claim MUST be in E.164 format and any extensions MUST be represented in RFC 3966 format.", + "type": "boolean", + "x-go-name": "PhoneNumberVerified" + }, + "picture": { + "description": "URL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), rather than to a Web page containing an image. Note that this URL SHOULD specifically reference a profile photo of the End-User suitable for displaying when describing the End-User, rather than an arbitrary photo taken by the End-User.", + "type": "string", + "x-go-name": "Picture" + }, + "preferred_username": { + "description": "Non-unique shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace.", + "type": "string", + "x-go-name": "PreferredUsername" + }, + "profile": { + "description": "URL of the End-User's profile page. The contents of this Web page SHOULD be about the End-User.", + "type": "string", + "x-go-name": "Profile" + }, + "sub": { + "description": "Subject - Identifier for the End-User at the IssuerURL.", + "type": "string", + "x-go-name": "Subject" + }, + "updated_at": { + "description": "Time the End-User's information was last updated. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time.", + "type": "integer", + "format": "int64", + "x-go-name": "UpdatedAt" + }, + "website": { + "description": "URL of the End-User's Web page or blog. This Web page SHOULD contain information published by the End-User or an organization that the End-User is affiliated with.", + "type": "string", + "x-go-name": "Website" + }, + "zoneinfo": { + "description": "String from zoneinfo [zoneinfo] time zone database representing the End-User's time zone. For example, Europe/Paris or America/Los_Angeles.", + "type": "string", + "x-go-name": "Zoneinfo" + } + }, + "x-go-name": "swaggeruserinfoResponsePayload", + "x-go-package": "github.com/ory/hydra/oauth2" + }, + "version": { + "type": "object", + "properties": { + "version": { + "description": "Version is the service's version.", + "type": "string", + "x-go-name": "Version" + } + }, + "x-go-name": "swaggerVersion", + "x-go-package": "github.com/ory/x/healthx" + }, + "wellKnown": { + "type": "object", + "required": [ + "issuer", + "authorization_endpoint", + "token_endpoint", + "jwks_uri", + "subject_types_supported", + "response_types_supported", + "id_token_signing_alg_values_supported" + ], + "properties": { + "authorization_endpoint": { + "description": "URL of the OP's OAuth 2.0 Authorization Endpoint.", + "type": "string", + "x-go-name": "AuthURL" + }, + "claims_parameter_supported": { + "description": "Boolean value specifying whether the OP supports use of the claims parameter, with true indicating support.", + "type": "boolean", + "x-go-name": "ClaimsParameterSupported" + }, + "claims_supported": { + "description": "JSON array containing a list of the Claim Names of the Claims that the OpenID Provider MAY be able to supply\nvalues for. Note that for privacy or other reasons, this might not be an exhaustive list.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "ClaimsSupported" + }, + "grant_types_supported": { + "description": "JSON array containing a list of the OAuth 2.0 Grant Type values that this OP supports.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "GrantTypesSupported" + }, + "id_token_signing_alg_values_supported": { + "description": "JSON array containing a list of the JWS signing algorithms (alg values) supported by the OP for the ID Token\nto encode the Claims in a JWT.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "IDTokenSigningAlgValuesSupported" + }, + "issuer": { + "description": "URL using the https scheme with no query or fragment component that the OP asserts as its IssuerURL Identifier.\nIf IssuerURL discovery is supported , this value MUST be identical to the issuer value returned\nby WebFinger. This also MUST be identical to the iss Claim value in ID Tokens issued from this IssuerURL.", + "type": "string", + "x-go-name": "Issuer" + }, + "jwks_uri": { + "description": "URL of the OP's JSON Web Key Set [JWK] document. This contains the signing key(s) the RP uses to validate\nsignatures from the OP. The JWK Set MAY also contain the Server's encryption key(s), which are used by RPs\nto encrypt requests to the Server. When both signing and encryption keys are made available, a use (Key Use)\nparameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage.\nAlthough some algorithms allow the same key to be used for both signatures and encryption, doing so is\nNOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of\nkeys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.", + "type": "string", + "x-go-name": "JWKsURI" + }, + "registration_endpoint": { + "description": "URL of the OP's Dynamic Client Registration Endpoint.", + "type": "string", + "x-go-name": "RegistrationEndpoint" + }, + "request_parameter_supported": { + "description": "Boolean value specifying whether the OP supports use of the request parameter, with true indicating support.", + "type": "boolean", + "x-go-name": "RequestParameterSupported" + }, + "request_uri_parameter_supported": { + "description": "Boolean value specifying whether the OP supports use of the request_uri parameter, with true indicating support.", + "type": "boolean", + "x-go-name": "RequestURIParameterSupported" + }, + "require_request_uri_registration": { + "description": "Boolean value specifying whether the OP requires any request_uri values used to be pre-registered\nusing the request_uris registration parameter.", + "type": "boolean", + "x-go-name": "RequireRequestURIRegistration" + }, + "response_modes_supported": { + "description": "JSON array containing a list of the OAuth 2.0 response_mode values that this OP supports.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "ResponseModesSupported" + }, + "response_types_supported": { + "description": "JSON array containing a list of the OAuth 2.0 response_type values that this OP supports. Dynamic OpenID\nProviders MUST support the code, id_token, and the token id_token Response Type values.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "ResponseTypes" + }, + "scopes_supported": { + "description": "SON array containing a list of the OAuth 2.0 [RFC6749] scope values that this server supports. The server MUST\nsupport the openid scope value. Servers MAY choose not to advertise some supported scope values even when this parameter is used", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "ScopesSupported" + }, + "subject_types_supported": { + "description": "JSON array containing a list of the Subject Identifier types that this OP supports. Valid types include\npairwise and public.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "SubjectTypes" + }, + "token_endpoint": { + "description": "URL of the OP's OAuth 2.0 Token Endpoint", + "type": "string", + "x-go-name": "TokenURL" + }, + "token_endpoint_auth_methods_supported": { + "description": "JSON array containing a list of Client Authentication methods supported by this Token Endpoint. The options are\nclient_secret_post, client_secret_basic, client_secret_jwt, and private_key_jwt, as described in Section 9 of OpenID Connect Core 1.0", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "TokenEndpointAuthMethodsSupported" + }, + "userinfo_endpoint": { + "description": "URL of the OP's UserInfo Endpoint.", + "type": "string", + "x-go-name": "UserinfoEndpoint" + }, + "userinfo_signing_alg_values_supported": { + "description": "JSON array containing a list of the JWS [JWS] signing algorithms (alg values) [JWA] supported by the UserInfo Endpoint to encode the Claims in a JWT [JWT].", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "UserinfoSigningAlgValuesSupported" + } + }, + "x-go-name": "WellKnown", + "x-go-package": "github.com/ory/hydra/oauth2" + } + }, + "responses": { + "emptyResponse": { + "description": "An empty response" + }, + "genericError": { + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "error": { + "type": "string", + "x-go-name": "Name" + }, + "error_code": { + "type": "integer", + "format": "int64", + "x-go-name": "Code" + }, + "error_debug": { + "type": "integer", + "format": "int64", + "x-go-name": "Debug" + }, + "error_hint": { + "type": "string", + "x-go-name": "Hint" + } + } + } + }, + "handledConsentRequestList": { + "description": "A list of handled consent requests.", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/PreviousConsentSession" + } + } + }, + "oAuth2ClientList": { + "description": "A list of clients.", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/oAuth2Client" + } + } + } + }, + "securityDefinitions": { + "basic": { + "type": "basic" + }, + "oauth2": { + "type": "oauth2", + "flow": "accessCode", + "authorizationUrl": "https://your-hydra-instance.com/oauth2/auth", + "tokenUrl": "https://your-hydra-instance.com/oauth2/token", + "scopes": { + "offline": "A scope required when requesting refresh tokens", + "openid": "Request an OpenID Connect ID Token" + } + } + }, + "x-forwarded-proto": "string", + "x-request-id": "string" +} \ No newline at end of file diff --git a/_legacy/_master/apis/keto.json b/_legacy/_master/apis/keto.json new file mode 100644 index 000000000..5721529f3 --- /dev/null +++ b/_legacy/_master/apis/keto.json @@ -0,0 +1,1577 @@ +{ + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "swagger": "2.0", + "info": { + "description": "Package main ORY Keto", + "contact": { + "name": "ORY", + "url": "https://www.ory.sh", + "email": "hi@ory.am" + }, + "license": { + "name": "Apache 2.0", + "url": "https://github.com/ory/keto/blob/master/LICENSE" + }, + "version": "Latest" + }, + "basePath": "/", + "paths": { + "/health/alive": { + "get": { + "description": "This endpoint returns a 200 status code when the HTTP server is up running.\nThis status does currently not include checks whether the database connection is working.\nThis endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set.\n\nBe aware that if you are running multiple nodes of ORY Keto, the health status will never refer to the cluster state, only to a single instance.", + "tags": [ + "health" + ], + "summary": "Check the Alive Status", + "operationId": "isInstanceAlive", + "responses": { + "200": { + "description": "healthStatus", + "schema": { + "$ref": "#/definitions/healthStatus" + } + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/health/ready": { + "get": { + "description": "This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g.\nthe database) are responsive as well.\n\nThis status does currently not include checks whether the database connection is working.\nThis endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set.\n\nBe aware that if you are running multiple nodes of ORY Keto, the health status will never refer to the cluster state, only to a single instance.", + "tags": [ + "health" + ], + "summary": "Check the Readiness Status", + "operationId": "isInstanceReady", + "responses": { + "200": { + "description": "healthStatus", + "schema": { + "$ref": "#/definitions/healthStatus" + } + }, + "503": { + "description": "healthNotReadyStatus", + "schema": { + "$ref": "#/definitions/healthNotReadyStatus" + } + } + } + } + }, + "/policies": { + "get": { + "description": "List Access Control Policies", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "policy" + ], + "operationId": "listPolicies", + "parameters": [ + { + "type": "integer", + "format": "int64", + "x-go-name": "Offset", + "description": "The offset from where to start looking.", + "name": "offset", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "x-go-name": "Limit", + "description": "The maximum amount of policies returned.", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/policyList" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "post": { + "description": "Create an Access Control Policy", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "policy" + ], + "operationId": "createPolicy", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/policy" + } + } + ], + "responses": { + "201": { + "description": "policy", + "schema": { + "$ref": "#/definitions/policy" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/policies/{id}": { + "get": { + "description": "Get an Access Control Policy", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "policy" + ], + "operationId": "getPolicy", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the policy.", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "policy", + "schema": { + "$ref": "#/definitions/policy" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "put": { + "description": "Update an Access Control Policy", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "policy" + ], + "operationId": "updatePolicy", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the policy.", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/policy" + } + } + ], + "responses": { + "200": { + "description": "policy", + "schema": { + "$ref": "#/definitions/policy" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "Delete an Access Control Policy", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "policy" + ], + "operationId": "deletePolicy", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the policy.", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/roles": { + "get": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to retrieve all roles that are stored in the system.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "List all roles", + "operationId": "listRoles", + "parameters": [ + { + "type": "string", + "x-go-name": "Member", + "description": "The id of the member to look up.", + "name": "member", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "x-go-name": "Limit", + "description": "The maximum amount of policies returned.", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "x-go-name": "Offset", + "description": "The offset from where to start looking.", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/listRolesResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "post": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to create a new role. You may define members as well but you don't have to.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "Create a role", + "operationId": "createRole", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/role" + } + } + ], + "responses": { + "201": { + "description": "role", + "schema": { + "$ref": "#/definitions/role" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/roles/{id}": { + "get": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to retrieve an existing role. You have to know the role's ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "Get a role by its ID", + "operationId": "getRole", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the role to look up.", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "role", + "schema": { + "$ref": "#/definitions/role" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "put": { + "description": "This endpoint allows you to overwrite a role. You have to know the role's ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.", + "operationId": "setRole", + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to delete an existing role. You have to know the role's ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "Get a role by its ID", + "operationId": "deleteRole", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the role to look up.", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/roles/{id}/members": { + "post": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to add members (users, applications, ...) to a specific role. You have to know the role's ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "Add members to a role", + "operationId": "addMembersToRole", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the role to modify.", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/roleMembers" + } + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to remove members (users, applications, ...) from a specific role. You have to know the role's ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "Remove members from a role", + "operationId": "removeMembersFromRole", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the role to modify.", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/roleMembers" + } + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/version": { + "get": { + "description": "This endpoint returns the version as `{ \"version\": \"VERSION\" }`. The version is only correct with the prebuilt binary and not custom builds.", + "tags": [ + "version" + ], + "summary": "Get the version of Keto", + "operationId": "getVersion", + "responses": { + "200": { + "description": "version", + "schema": { + "$ref": "#/definitions/version" + } + } + } + } + }, + "/warden/oauth2/access-tokens/authorize": { + "post": { + "description": "Checks if a token is valid and if the token subject is allowed to perform an action on a resource.\nThis endpoint requires a token, a scope, a resource name, an action name and a context.\n\n\nIf a token is expired/invalid, has not been granted the requested scope or the subject is not allowed to\nperform the action on the resource, this endpoint returns a 200 response with `{ \"allowed\": false }`.\n\n\nThis endpoint passes all data from the upstream OAuth 2.0 token introspection endpoint. If you use ORY Hydra as an\nupstream OAuth 2.0 provider, data set through the `accessTokenExtra` field in the consent flow will be included in this\nresponse as well.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "warden" + ], + "summary": "Check if an OAuth 2.0 access token is authorized to access a resource", + "operationId": "isOAuth2AccessTokenAuthorized", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/wardenOAuth2AccessTokenAuthorizationRequest" + } + } + ], + "responses": { + "200": { + "description": "wardenOAuth2AccessTokenAuthorizationResponse", + "schema": { + "$ref": "#/definitions/wardenOAuth2AccessTokenAuthorizationResponse" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/warden/oauth2/clients/authorize": { + "post": { + "description": "Checks if an OAuth 2.0 Client provided the correct access credentials and and if the client is allowed to perform\nan action on a resource. This endpoint requires a client id, a client secret, a scope, a resource name, an action name and a context.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "warden" + ], + "summary": "Check if an OAuth 2.0 Client is authorized to access a resource", + "operationId": "isOAuth2ClientAuthorized", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/wardenOAuth2ClientAuthorizationRequest" + } + } + ], + "responses": { + "200": { + "description": "wardenOAuth2ClientAuthorizationResponse", + "schema": { + "$ref": "#/definitions/wardenOAuth2ClientAuthorizationResponse" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/warden/subjects/authorize": { + "post": { + "description": "Checks if a subject (e.g. user ID, API key, ...) is allowed to perform a certain action on a resource.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "warden" + ], + "summary": "Check if a subject is authorized to access a resource", + "operationId": "isSubjectAuthorized", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/WardenSubjectAuthorizationRequest" + } + } + ], + "responses": { + "200": { + "description": "wardenSubjectAuthorizationResponse", + "schema": { + "$ref": "#/definitions/wardenSubjectAuthorizationResponse" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + } + }, + "definitions": { + "AuthenticationOAuth2ClientCredentialsRequest": { + "type": "object", + "properties": { + "client_id": { + "description": "Token is the token to introspect.", + "type": "string", + "x-go-name": "ClientID" + }, + "client_secret": { + "type": "string", + "x-go-name": "ClientSecret" + }, + "scope": { + "description": "Scope is an array of scopes that are required.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Scopes" + } + }, + "x-go-package": "github.com/ory/keto/authentication" + }, + "AuthenticationOAuth2IntrospectionRequest": { + "type": "object", + "properties": { + "scope": { + "description": "Scope is an array of scopes that are required.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Scope" + }, + "token": { + "description": "Token is the token to introspect.", + "type": "string", + "x-go-name": "Token" + } + }, + "x-go-package": "github.com/ory/keto/authentication" + }, + "Authenticator": { + "type": "object", + "x-go-package": "github.com/ory/keto/authentication" + }, + "Firewall": { + "type": "object", + "title": "Firewall offers various validation strategies for access tokens.", + "x-go-package": "github.com/ory/keto/warden" + }, + "Handler": { + "type": "object", + "properties": { + "H": { + "$ref": "#/definitions/Writer" + }, + "Manager": { + "$ref": "#/definitions/Manager" + } + }, + "x-go-package": "github.com/ory/keto/role" + }, + "IntrospectionResponse": { + "type": "object", + "properties": { + "active": { + "type": "boolean", + "x-go-name": "Active" + }, + "aud": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Audience" + }, + "client_id": { + "type": "string", + "x-go-name": "ClientID" + }, + "exp": { + "type": "integer", + "format": "int64", + "x-go-name": "ExpiresAt" + }, + "ext": { + "description": "Session represents arbitrary session data.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Extra" + }, + "iat": { + "type": "integer", + "format": "int64", + "x-go-name": "IssuedAt" + }, + "iss": { + "type": "string", + "x-go-name": "Issuer" + }, + "nbf": { + "type": "integer", + "format": "int64", + "x-go-name": "NotBefore" + }, + "scope": { + "type": "string", + "x-go-name": "Scope" + }, + "sub": { + "description": "Here, it's sub", + "type": "string", + "x-go-name": "Subject" + }, + "token_type": { + "type": "string", + "x-go-name": "TokenType" + }, + "username": { + "type": "string", + "x-go-name": "Username" + } + }, + "x-go-package": "github.com/ory/keto/authentication" + }, + "Manager": { + "type": "object", + "x-go-package": "github.com/ory/keto/role" + }, + "OAuth2ClientCredentialsAuthentication": { + "type": "object", + "x-go-package": "github.com/ory/keto/authentication" + }, + "OAuth2IntrospectionAuthentication": { + "type": "object", + "x-go-package": "github.com/ory/keto/authentication" + }, + "Session": { + "type": "object", + "properties": { + "GetSubject": { + "type": "string" + } + }, + "x-go-package": "github.com/ory/keto/authentication" + }, + "WardenSubjectAuthorizationRequest": { + "type": "object", + "title": "AccessRequest is the warden's request object.", + "properties": { + "action": { + "description": "Action is the action that is requested on the resource.", + "type": "string", + "x-go-name": "Action" + }, + "context": { + "description": "Context is the request's environmental context.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Context" + }, + "resource": { + "description": "Resource is the resource that access is requested to.", + "type": "string", + "x-go-name": "Resource" + }, + "subject": { + "description": "Subejct is the subject that is requesting access.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "AccessRequest", + "x-go-package": "github.com/ory/keto/warden" + }, + "Writer": { + "description": "Writer is a helper to write arbitrary data to a ResponseWriter", + "type": "object", + "x-go-package": "github.com/ory/keto/vendor/github.com/ory/herodot" + }, + "authenticationDefaultSession": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "DefaultSession", + "x-go-package": "github.com/ory/keto/authentication" + }, + "authenticationOAuth2ClientCredentialsSession": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "OAuth2ClientCredentialsSession", + "x-go-package": "github.com/ory/keto/authentication" + }, + "authenticationOAuth2Session": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "aud": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Audience" + }, + "client_id": { + "description": "ClientID is the id of the OAuth2 client that requested the token.", + "type": "string", + "x-go-name": "ClientID" + }, + "exp": { + "description": "ExpiresAt is the expiry timestamp.", + "type": "string", + "format": "date-time", + "x-go-name": "ExpiresAt" + }, + "iat": { + "description": "IssuedAt is the token creation time stamp.", + "type": "string", + "format": "date-time", + "x-go-name": "IssuedAt" + }, + "iss": { + "description": "Issuer is the id of the issuer, typically an hydra instance.", + "type": "string", + "x-go-name": "Issuer" + }, + "nbf": { + "type": "string", + "format": "date-time", + "x-go-name": "NotBefore" + }, + "scope": { + "description": "GrantedScopes is a list of scopes that the subject authorized when asked for consent.", + "type": "string", + "x-go-name": "GrantedScopes" + }, + "session": { + "description": "Session represents arbitrary session data.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Extra" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + }, + "username": { + "type": "string", + "x-go-name": "Username" + } + }, + "x-go-name": "OAuth2Session", + "x-go-package": "github.com/ory/keto/authentication" + }, + "healthNotReadyStatus": { + "type": "object", + "properties": { + "errors": { + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "x-go-name": "Errors" + } + }, + "x-go-name": "swaggerNotReadyStatus", + "x-go-package": "github.com/ory/keto/health" + }, + "healthStatus": { + "type": "object", + "properties": { + "status": { + "description": "Status always contains \"ok\".", + "type": "string", + "x-go-name": "Status" + } + }, + "x-go-name": "swaggerHealthStatus", + "x-go-package": "github.com/ory/keto/health" + }, + "policy": { + "type": "object", + "properties": { + "actions": { + "description": "Actions impacted by the policy.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Actions" + }, + "conditions": { + "description": "Conditions under which the policy is active.", + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "options": { + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Options" + }, + "type": { + "type": "string", + "x-go-name": "Type" + } + } + }, + "x-go-name": "Conditions" + }, + "description": { + "description": "Description of the policy.", + "type": "string", + "x-go-name": "Description" + }, + "effect": { + "description": "Effect of the policy", + "type": "string", + "x-go-name": "Effect" + }, + "id": { + "description": "ID of the policy.", + "type": "string", + "x-go-name": "ID" + }, + "resources": { + "description": "Resources impacted by the policy.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Resources" + }, + "subjects": { + "description": "Subjects impacted by the policy.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Subjects" + } + }, + "x-go-name": "swaggerPolicy", + "x-go-package": "github.com/ory/keto/policy" + }, + "role": { + "description": "Role represents a group of users that share the same role. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.", + "type": "object", + "properties": { + "id": { + "description": "ID is the role's unique id.", + "type": "string", + "x-go-name": "ID" + }, + "members": { + "description": "Members is who belongs to the role.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Members" + } + }, + "x-go-name": "Role", + "x-go-package": "github.com/ory/keto/role" + }, + "roleMembers": { + "type": "object", + "properties": { + "members": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Members" + } + }, + "x-go-name": "membersRequest", + "x-go-package": "github.com/ory/keto/role" + }, + "swaggerCreatePolicyParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/policy" + } + }, + "x-go-package": "github.com/ory/keto/policy" + }, + "swaggerDoesWardenAllowAccessRequestParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/WardenSubjectAuthorizationRequest" + } + }, + "x-go-package": "github.com/ory/keto/warden" + }, + "swaggerDoesWardenAllowClientRequestParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/wardenOAuth2ClientAuthorizationRequest" + } + }, + "x-go-package": "github.com/ory/keto/warden" + }, + "swaggerDoesWardenAllowTokenAccessRequestParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/wardenOAuth2AccessTokenAuthorizationRequest" + } + }, + "x-go-package": "github.com/ory/keto/warden" + }, + "swaggerGetPolicyParameters": { + "type": "object", + "properties": { + "id": { + "description": "The id of the policy.\nin: path", + "type": "string", + "x-go-name": "ID" + } + }, + "x-go-package": "github.com/ory/keto/policy" + }, + "swaggerListPolicyParameters": { + "type": "object", + "properties": { + "limit": { + "description": "The maximum amount of policies returned.\nin: query", + "type": "integer", + "format": "int64", + "x-go-name": "Limit" + }, + "offset": { + "description": "The offset from where to start looking.\nin: query", + "type": "integer", + "format": "int64", + "x-go-name": "Offset" + } + }, + "x-go-package": "github.com/ory/keto/policy" + }, + "swaggerListPolicyResponse": { + "description": "A policy", + "type": "object", + "properties": { + "Body": { + "description": "in: body\ntype: array", + "type": "array", + "items": { + "$ref": "#/definitions/policy" + } + } + }, + "x-go-package": "github.com/ory/keto/policy" + }, + "swaggerUpdatePolicyParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/policy" + }, + "id": { + "description": "The id of the policy.\nin: path", + "type": "string", + "x-go-name": "ID" + } + }, + "x-go-package": "github.com/ory/keto/policy" + }, + "swaggerWardenBaseRequest": { + "description": "swager:model authorizedBaseRequest", + "type": "object", + "properties": { + "action": { + "description": "Action is the action that is requested on the resource.", + "type": "string", + "x-go-name": "Action" + }, + "context": { + "description": "Context is the request's environmental context.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Context" + }, + "resource": { + "description": "Resource is the resource that access is requested to.", + "type": "string", + "x-go-name": "Resource" + } + }, + "x-go-package": "github.com/ory/keto/warden" + }, + "version": { + "type": "object", + "properties": { + "version": { + "type": "string", + "x-go-name": "Version" + } + }, + "x-go-name": "swaggerVersion", + "x-go-package": "github.com/ory/keto/health" + }, + "wardenOAuth2AccessTokenAuthorizationRequest": { + "type": "object", + "properties": { + "action": { + "description": "Action is the action that is requested on the resource.", + "type": "string", + "x-go-name": "Action" + }, + "context": { + "description": "Context is the request's environmental context.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Context" + }, + "resource": { + "description": "Resource is the resource that access is requested to.", + "type": "string", + "x-go-name": "Resource" + }, + "scope": { + "description": "Scope is an array of scopes that are required.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Scope" + }, + "token": { + "description": "Token is the token to introspect.", + "type": "string", + "x-go-name": "Token" + } + }, + "x-go-name": "swaggerWardenTokenAccessRequest", + "x-go-package": "github.com/ory/keto/warden" + }, + "wardenOAuth2AccessTokenAuthorizationResponse": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "aud": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Audience" + }, + "client_id": { + "description": "ClientID is the id of the OAuth2 client that requested the token.", + "type": "string", + "x-go-name": "ClientID" + }, + "exp": { + "description": "ExpiresAt is the expiry timestamp.", + "type": "string", + "format": "date-time", + "x-go-name": "ExpiresAt" + }, + "iat": { + "description": "IssuedAt is the token creation time stamp.", + "type": "string", + "format": "date-time", + "x-go-name": "IssuedAt" + }, + "iss": { + "description": "Issuer is the id of the issuer, typically an hydra instance.", + "type": "string", + "x-go-name": "Issuer" + }, + "nbf": { + "type": "string", + "format": "date-time", + "x-go-name": "NotBefore" + }, + "scope": { + "description": "GrantedScopes is a list of scopes that the subject authorized when asked for consent.", + "type": "string", + "x-go-name": "GrantedScopes" + }, + "session": { + "description": "Session represents arbitrary session data.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Extra" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + }, + "username": { + "type": "string", + "x-go-name": "Username" + } + }, + "x-go-name": "oauth2Authorization", + "x-go-package": "github.com/ory/keto/warden" + }, + "wardenOAuth2ClientAuthorizationRequest": { + "type": "object", + "properties": { + "action": { + "description": "Action is the action that is requested on the resource.", + "type": "string", + "x-go-name": "Action" + }, + "client_id": { + "description": "Token is the token to introspect.", + "type": "string", + "x-go-name": "ClientID" + }, + "client_secret": { + "type": "string", + "x-go-name": "ClientSecret" + }, + "context": { + "description": "Context is the request's environmental context.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Context" + }, + "resource": { + "description": "Resource is the resource that access is requested to.", + "type": "string", + "x-go-name": "Resource" + }, + "scope": { + "description": "Scope is an array of scopes that are required.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Scopes" + } + }, + "x-go-name": "swaggerWardenClientAccessRequest", + "x-go-package": "github.com/ory/keto/warden" + }, + "wardenOAuth2ClientAuthorizationResponse": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "oauth2ClientAuthorization", + "x-go-package": "github.com/ory/keto/warden" + }, + "wardenSubjectAuthorizationResponse": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "subjectAuthorization", + "x-go-package": "github.com/ory/keto/warden" + } + }, + "responses": { + "emptyResponse": { + "description": "An empty response" + }, + "genericError": { + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64", + "x-go-name": "Code" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "x-go-name": "Details" + }, + "message": { + "type": "string", + "x-go-name": "Message" + }, + "reason": { + "type": "string", + "x-go-name": "Reason" + }, + "request": { + "type": "string", + "x-go-name": "Request" + }, + "status": { + "type": "string", + "x-go-name": "Status" + } + } + } + }, + "listRolesResponse": { + "description": "A list of roles the member is belonging to", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/role" + } + } + }, + "policyList": { + "description": "A policy", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/policy" + } + } + } + } +} \ No newline at end of file diff --git a/_legacy/_master/apis/oathkeeper.json b/_legacy/_master/apis/oathkeeper.json new file mode 100644 index 000000000..d93264aa3 --- /dev/null +++ b/_legacy/_master/apis/oathkeeper.json @@ -0,0 +1,759 @@ +{ + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "swagger": "2.0", + "info": { + "description": "ORY Oathkeeper is a reverse proxy that checks the HTTP Authorization for validity against a set of rules. This service uses Hydra to validate access tokens and policies.", + "title": "ORY Oathkeeper", + "contact": { + "name": "ORY", + "url": "https://www.ory.am", + "email": "hi@ory.am" + }, + "version": "Latest" + }, + "basePath": "/", + "paths": { + "/.well-known/jwks.json": { + "get": { + "description": "This endpoint returns public keys for validating the ID tokens issued by ORY Oathkeeper.", + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "summary": "Returns well known keys", + "operationId": "getWellKnown", + "responses": { + "200": { + "description": "jsonWebKeySet", + "schema": { + "$ref": "#/definitions/jsonWebKeySet" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/health/alive": { + "get": { + "description": "This endpoint returns a 200 status code when the HTTP server is up running.\nThis status does currently not include checks whether the database connection is working.\nThis endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set.\n\nBe aware that if you are running multiple nodes of ORY Oathkeeper, the health status will never refer to the cluster state, only to a single instance.", + "tags": [ + "health" + ], + "summary": "Check the Alive Status", + "operationId": "isInstanceAlive", + "responses": { + "200": { + "description": "healthStatus", + "schema": { + "$ref": "#/definitions/healthStatus" + } + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/health/ready": { + "get": { + "description": "This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g.\nthe database) are responsive as well.\n\nThis status does currently not include checks whether the database connection is working.\nThis endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set.\n\nBe aware that if you are running multiple nodes of ORY Oathkeeper, the health status will never refer to the cluster state, only to a single instance.", + "tags": [ + "health" + ], + "summary": "Check the Readiness Status", + "operationId": "isInstanceReady", + "responses": { + "200": { + "description": "healthStatus", + "schema": { + "$ref": "#/definitions/healthStatus" + } + }, + "503": { + "description": "healthNotReadyStatus", + "schema": { + "$ref": "#/definitions/healthNotReadyStatus" + } + } + } + } + }, + "/judge": { + "get": { + "description": "This endpoint mirrors the proxy capability of ORY Oathkeeper's proxy functionality but instead of forwarding the\nrequest to the upstream server, returns 200 (request should be allowed), 401 (unauthorized), or 403 (forbidden)\nstatus codes. This endpoint can be used to integrate with other API Proxies like Ambassador, Kong, Envoy, and many more.", + "schemes": [ + "http", + "https" + ], + "tags": [ + "judge" + ], + "summary": "Judge if a request should be allowed or not", + "operationId": "judge", + "responses": { + "200": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/rules": { + "get": { + "description": "This method returns an array of all rules that are stored in the backend. This is useful if you want to get a full\nview of what rules you have currently in place.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "rule" + ], + "summary": "List all rules", + "operationId": "listRules", + "parameters": [ + { + "type": "integer", + "format": "int64", + "x-go-name": "Limit", + "description": "The maximum amount of rules returned.", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "x-go-name": "Offset", + "description": "The offset from where to start looking.", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/rules" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "post": { + "description": "This method allows creation of rules. If a rule id exists, you will receive an error.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "rule" + ], + "summary": "Create a rule", + "operationId": "createRule", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/rule" + } + } + ], + "responses": { + "201": { + "$ref": "#/responses/rule" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/rules/{id}": { + "get": { + "description": "Use this method to retrieve a rule from the storage. If it does not exist you will receive a 404 error.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "rule" + ], + "summary": "Retrieve a rule", + "operationId": "getRule", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/rule" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "put": { + "description": "Use this method to update a rule. Keep in mind that you need to send the full rule payload as this endpoint does\nnot support patching.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "rule" + ], + "summary": "Update a rule", + "operationId": "updateRule", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/rule" + } + } + ], + "responses": { + "200": { + "$ref": "#/responses/rule" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "Use this endpoint to delete a rule.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "rule" + ], + "summary": "Delete a rule", + "operationId": "deleteRule", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "404": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/version": { + "get": { + "description": "This endpoint returns the version as `{ \"version\": \"VERSION\" }`. The version is only correct with the prebuilt binary and not custom builds.", + "tags": [ + "version" + ], + "summary": "Get the version of Oathkeeper", + "operationId": "getVersion", + "responses": { + "200": { + "description": "version", + "schema": { + "$ref": "#/definitions/version" + } + } + } + } + } + }, + "definitions": { + "Upstream": { + "type": "object", + "properties": { + "preserve_host": { + "description": "PreserveHost, if false (the default), tells ORY Oathkeeper to set the upstream request's Host header to the\nhostname of the API's upstream's URL. Setting this flag to true instructs ORY Oathkeeper not to do so.", + "type": "boolean", + "x-go-name": "PreserveHost" + }, + "strip_path": { + "description": "StripPath if set, replaces the provided path prefix when forwarding the requested URL to the upstream URL.", + "type": "string", + "x-go-name": "StripPath" + }, + "url": { + "description": "URL is the URL the request will be proxied to.", + "type": "string", + "x-go-name": "URL" + } + }, + "x-go-package": "github.com/ory/oathkeeper/rule" + }, + "healthNotReadyStatus": { + "type": "object", + "properties": { + "errors": { + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "x-go-name": "Errors" + } + }, + "x-go-name": "swaggerNotReadyStatus", + "x-go-package": "github.com/ory/oathkeeper/health" + }, + "healthStatus": { + "type": "object", + "properties": { + "status": { + "description": "Status always contains \"ok\".", + "type": "string", + "x-go-name": "Status" + } + }, + "x-go-name": "swaggerHealthStatus", + "x-go-package": "github.com/ory/oathkeeper/health" + }, + "jsonWebKey": { + "type": "object", + "properties": { + "alg": { + "description": "The \"alg\" (algorithm) parameter identifies the algorithm intended for\nuse with the key. The values used should either be registered in the\nIANA \"JSON Web Signature and Encryption Algorithms\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name.", + "type": "string", + "x-go-name": "Alg" + }, + "crv": { + "type": "string", + "x-go-name": "Crv" + }, + "d": { + "type": "string", + "x-go-name": "D" + }, + "dp": { + "type": "string", + "x-go-name": "Dp" + }, + "dq": { + "type": "string", + "x-go-name": "Dq" + }, + "e": { + "type": "string", + "x-go-name": "E" + }, + "k": { + "type": "string", + "x-go-name": "K" + }, + "kid": { + "description": "The \"kid\" (key ID) parameter is used to match a specific key. This\nis used, for instance, to choose among a set of keys within a JWK Set\nduring key rollover. The structure of the \"kid\" value is\nunspecified. When \"kid\" values are used within a JWK Set, different\nkeys within the JWK Set SHOULD use distinct \"kid\" values. (One\nexample in which different keys might use the same \"kid\" value is if\nthey have different \"kty\" (key type) values but are considered to be\nequivalent alternatives by the application using them.) The \"kid\"\nvalue is a case-sensitive string.", + "type": "string", + "x-go-name": "Kid" + }, + "kty": { + "description": "The \"kty\" (key type) parameter identifies the cryptographic algorithm\nfamily used with the key, such as \"RSA\" or \"EC\". \"kty\" values should\neither be registered in the IANA \"JSON Web Key Types\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name. The \"kty\" value is a case-sensitive string.", + "type": "string", + "x-go-name": "Kty" + }, + "n": { + "type": "string", + "x-go-name": "N" + }, + "p": { + "type": "string", + "x-go-name": "P" + }, + "q": { + "type": "string", + "x-go-name": "Q" + }, + "qi": { + "type": "string", + "x-go-name": "Qi" + }, + "use": { + "description": "The \"use\" (public key use) parameter identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Values are commonly \"sig\" (signature) or \"enc\" (encryption).", + "type": "string", + "x-go-name": "Use" + }, + "x": { + "type": "string", + "x-go-name": "X" + }, + "x5c": { + "description": "The \"x5c\" (X.509 certificate chain) parameter contains a chain of one\nor more PKIX certificates [RFC5280]. The certificate chain is\nrepresented as a JSON array of certificate value strings. Each\nstring in the array is a base64-encoded (Section 4 of [RFC4648] --\nnot base64url-encoded) DER [ITU.X690.1994] PKIX certificate value.\nThe PKIX certificate containing the key value MUST be the first\ncertificate.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "X5c" + }, + "y": { + "type": "string", + "x-go-name": "Y" + } + }, + "x-go-name": "swaggerJSONWebKey", + "x-go-package": "github.com/ory/oathkeeper/rsakey" + }, + "jsonWebKeySet": { + "type": "object", + "properties": { + "keys": { + "description": "The value of the \"keys\" parameter is an array of JWK values. By\ndefault, the order of the JWK values within the array does not imply\nan order of preference among them, although applications of JWK Sets\ncan choose to assign a meaning to the order for their purposes, if\ndesired.", + "type": "array", + "items": { + "$ref": "#/definitions/jsonWebKey" + }, + "x-go-name": "Keys" + } + }, + "x-go-name": "swaggerJSONWebKeySet", + "x-go-package": "github.com/ory/oathkeeper/rsakey" + }, + "rule": { + "type": "object", + "title": "swaggerRule is a single rule that will get checked on every HTTP request.", + "properties": { + "authenticators": { + "description": "Authenticators is a list of authentication handlers that will try and authenticate the provided credentials.\nAuthenticators are checked iteratively from index 0 to n and if the first authenticator to return a positive\nresult will be the one used.\n\nIf you want the rule to first check a specific authenticator before \"falling back\" to others, have that authenticator\nas the first item in the array.", + "type": "array", + "items": { + "$ref": "#/definitions/ruleHandler" + }, + "x-go-name": "Authenticators" + }, + "authorizer": { + "$ref": "#/definitions/ruleHandler" + }, + "credentials_issuer": { + "$ref": "#/definitions/ruleHandler" + }, + "description": { + "description": "Description is a human readable description of this rule.", + "type": "string", + "x-go-name": "Description" + }, + "id": { + "description": "ID is the unique id of the rule. It can be at most 190 characters long, but the layout of the ID is up to you.\nYou will need this ID later on to update or delete the rule.", + "type": "string", + "x-go-name": "ID" + }, + "match": { + "$ref": "#/definitions/ruleMatch" + }, + "upstream": { + "$ref": "#/definitions/Upstream" + } + }, + "x-go-name": "swaggerRule", + "x-go-package": "github.com/ory/oathkeeper/rule" + }, + "ruleHandler": { + "type": "object", + "properties": { + "config": { + "description": "Config contains the configuration for the handler. Please read the user\nguide for a complete list of each handler's available settings.", + "type": "string", + "x-go-name": "Config" + }, + "handler": { + "description": "Handler identifies the implementation which will be used to handle this specific request. Please read the user\nguide for a complete list of available handlers.", + "type": "string", + "x-go-name": "Handler" + } + }, + "x-go-name": "swaggerRuleHandler", + "x-go-package": "github.com/ory/oathkeeper/rule" + }, + "ruleMatch": { + "type": "object", + "properties": { + "methods": { + "description": "An array of HTTP methods (e.g. GET, POST, PUT, DELETE, ...). When ORY Oathkeeper searches for rules\nto decide what to do with an incoming request to the proxy server, it compares the HTTP method of the incoming\nrequest with the HTTP methods of each rules. If a match is found, the rule is considered a partial match.\nIf the matchesUrl field is satisfied as well, the rule is considered a full match.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Methods" + }, + "url": { + "description": "This field represents the URL pattern this rule matches. When ORY Oathkeeper searches for rules\nto decide what to do with an incoming request to the proxy server, it compares the full request URL\n(e.g. https://mydomain.com/api/resource) without query parameters of the incoming\nrequest with this field. If a match is found, the rule is considered a partial match.\nIf the matchesMethods field is satisfied as well, the rule is considered a full match.\n\nYou can use regular expressions in this field to match more than one url. Regular expressions are encapsulated in\nbrackets \u003c and \u003e. The following example matches all paths of the domain `mydomain.com`: `https://mydomain.com/\u003c.*\u003e`.", + "type": "string", + "x-go-name": "URL" + } + }, + "x-go-name": "swaggerRuleMatch", + "x-go-package": "github.com/ory/oathkeeper/rule" + }, + "swaggerCreateRuleParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/rule" + } + }, + "x-go-package": "github.com/ory/oathkeeper/rule" + }, + "swaggerGetRuleParameters": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "id": { + "description": "in: path", + "type": "string", + "x-go-name": "ID" + } + }, + "x-go-package": "github.com/ory/oathkeeper/rule" + }, + "swaggerListRulesParameters": { + "type": "object", + "properties": { + "limit": { + "description": "The maximum amount of rules returned.\nin: query", + "type": "integer", + "format": "int64", + "x-go-name": "Limit" + }, + "offset": { + "description": "The offset from where to start looking.\nin: query", + "type": "integer", + "format": "int64", + "x-go-name": "Offset" + } + }, + "x-go-package": "github.com/ory/oathkeeper/rule" + }, + "swaggerRuleResponse": { + "description": "A rule", + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/rule" + } + }, + "x-go-package": "github.com/ory/oathkeeper/rule" + }, + "swaggerRulesResponse": { + "description": "A list of rules", + "type": "object", + "properties": { + "Body": { + "description": "in: body\ntype: array", + "type": "array", + "items": { + "$ref": "#/definitions/rule" + } + } + }, + "x-go-package": "github.com/ory/oathkeeper/rule" + }, + "swaggerUpdateRuleParameters": { + "type": "object", + "required": [ + "id" + ], + "properties": { + "Body": { + "$ref": "#/definitions/rule" + }, + "id": { + "description": "in: path", + "type": "string", + "x-go-name": "ID" + } + }, + "x-go-package": "github.com/ory/oathkeeper/rule" + }, + "version": { + "type": "object", + "properties": { + "version": { + "type": "string", + "x-go-name": "Version" + } + }, + "x-go-name": "swaggerVersion", + "x-go-package": "github.com/ory/oathkeeper/health" + } + }, + "responses": { + "emptyResponse": { + "description": "An empty response" + }, + "genericError": { + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64", + "x-go-name": "Code" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "x-go-name": "Details" + }, + "message": { + "type": "string", + "x-go-name": "Message" + }, + "reason": { + "type": "string", + "x-go-name": "Reason" + }, + "request": { + "type": "string", + "x-go-name": "Request" + }, + "status": { + "type": "string", + "x-go-name": "Status" + } + } + } + }, + "rule": { + "description": "A rule", + "schema": { + "$ref": "#/definitions/rule" + } + }, + "rules": { + "description": "A list of rules", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/rule" + } + } + } + }, + "x-forwarded-proto": "string", + "x-request-id": "string" +} \ No newline at end of file diff --git a/faq/README.md b/_legacy/_master/faq/README.md similarity index 100% rename from faq/README.md rename to _legacy/_master/faq/README.md diff --git a/faq/SUMMARY.md b/_legacy/_master/faq/SUMMARY.md similarity index 100% rename from faq/SUMMARY.md rename to _legacy/_master/faq/SUMMARY.md diff --git a/faq/_layouts/website/page.html b/_legacy/_master/faq/_layouts/website/page.html similarity index 100% rename from faq/_layouts/website/page.html rename to _legacy/_master/faq/_layouts/website/page.html diff --git a/faq/book.json b/_legacy/_master/faq/book.json similarity index 100% rename from faq/book.json rename to _legacy/_master/faq/book.json diff --git a/faq/general/consulting.md b/_legacy/_master/faq/general/consulting.md similarity index 100% rename from faq/general/consulting.md rename to _legacy/_master/faq/general/consulting.md diff --git a/faq/general/docs.md b/_legacy/_master/faq/general/docs.md similarity index 100% rename from faq/general/docs.md rename to _legacy/_master/faq/general/docs.md diff --git a/faq/general/envs.md b/_legacy/_master/faq/general/envs.md similarity index 100% rename from faq/general/envs.md rename to _legacy/_master/faq/general/envs.md diff --git a/faq/general/jobs.md b/_legacy/_master/faq/general/jobs.md similarity index 100% rename from faq/general/jobs.md rename to _legacy/_master/faq/general/jobs.md diff --git a/faq/general/jwt.md b/_legacy/_master/faq/general/jwt.md similarity index 100% rename from faq/general/jwt.md rename to _legacy/_master/faq/general/jwt.md diff --git a/faq/general/open-source.md b/_legacy/_master/faq/general/open-source.md similarity index 100% rename from faq/general/open-source.md rename to _legacy/_master/faq/general/open-source.md diff --git a/faq/hydra/lifespan.md b/_legacy/_master/faq/hydra/lifespan.md similarity index 100% rename from faq/hydra/lifespan.md rename to _legacy/_master/faq/hydra/lifespan.md diff --git a/faq/hydra/oauth2-tutorials.md b/_legacy/_master/faq/hydra/oauth2-tutorials.md similarity index 100% rename from faq/hydra/oauth2-tutorials.md rename to _legacy/_master/faq/hydra/oauth2-tutorials.md diff --git a/faq/hydra/scope-acp.md b/_legacy/_master/faq/hydra/scope-acp.md similarity index 100% rename from faq/hydra/scope-acp.md rename to _legacy/_master/faq/hydra/scope-acp.md diff --git a/faq/package.json b/_legacy/_master/faq/package.json similarity index 100% rename from faq/package.json rename to _legacy/_master/faq/package.json diff --git a/faq/yarn.lock b/_legacy/_master/faq/yarn.lock similarity index 100% rename from faq/yarn.lock rename to _legacy/_master/faq/yarn.lock diff --git a/_legacy/_master/guides/README.md b/_legacy/_master/guides/README.md new file mode 100644 index 000000000..d4b904382 --- /dev/null +++ b/_legacy/_master/guides/README.md @@ -0,0 +1,71 @@ +# ORY Developer Guide + +Welcome to the ORY Developer Guide! + + + +--- + +We provide an open source ecosystem of services with clear boundaries that solve authentication and authorization: + +- ORY Hydra is an OAuth 2.0 and OpenID Connect provider. +- ORY Oathkeeper is an Identity and Access Proxy. +- ORY Keto is an access control server. +- *TBA is an identity management server.* + +Each service works standalone but you can also combine them to get the full feature set. If you've never heard of an Identity & Access Proxy +before, or you want to learn more about the individual services and how they play together, stick with us through the +next sections. + +## The ORY Ecosystem + +Almost every application has the concept of users and permissions. An anonymous user, for example, is allowed to +read blog posts while certain authenticated users are allowed to write blog posts. While this is the basis for most +applications out there, access control becomes increasingly complex as an application grows. What started out with +a user's username and password now shifted to machine-2-machine interaction, third party developers accessing your +user's data, and maybe even a micro service system architecture. + +Our products solve the simplest use case and give you the ability to instantly ready the system for more complex scenarios without +painful and slow upgrade processes. + +The first service, which is to be announced soon, focuses on identity management. Instead of re-writing login, logout, +activation emails, 2fa, and worring about GDPR, you spin up a docker image and write a simple UI for it in the language +or framework of your choice. + +**ORY Hydra** enables you to become an OAuth 2.0 and OpenID Connect provider. If you're not writing a basic web app but something +that has to work on different devices, that has machine-2-machine interaction, or enables third-party developers to use +your API (and pay for it), then this is what you're looking for. ORY Hydra is not identity management, though. Instead, +it connects to your existing identity management (e.g. the one from the paragraph above, or your MySQL+PHP login service, +or your Federated SAML SSO) and is capable of issuing, in a secure and OpenID Certified manner, access, refresh, and ID tokens. +Of course, it's shipped as a 5MB Docker Image with almost no configuration required. + +Now that your users access your application through, for example, a React/Angular app and a REST api, you need a way +to authenticate the user and to check if he/she has the necessary permissions (we call this "access control" from now on). +One way would be, of course, to add these checks in your code. Another is to deploy the 5MB **ORY Oathkeeper** Docker Image, +define access rules for your API endpoints (e.g. OAuth 2.0 Access Token + certain set of permissions, a valid JSON Web Token, +a valid SAML assertion, ...) and put it - like a firewall - in front of your services. + +You might start out with a simple permission system. You've got different roles: anonymous users (not logged in), +authenticated users (logged in), and administrators. At some point however, the system gets more complex. You want +to distinct permissions based on the user's organization, the access time (think time lock in banking), or the billing +plan he/she's on. Big cloud providers such as Amazon Web Services or Google solve this using "Access Control Policies". +These policies represent flexible rules and allow you to express complex access control scenarios. You could, of course, +write your own system or spend a bit of time educating yourself about RBAC, ACL, ABAC, ACP - or (you probably already guessed it) - +boot up the 5MB **ORY Keto** Docker Image. ORY Keto is able to authenticate different types of credentials (e.g. OAuth 2.0 Access +Tokens, SAML Assertions, JSON Web Tokens, ...) and allows you to define advanced permission rules ("Access Control Policies"). +And there's of course an endpoint that tells you if a certain set of credentials (e.g. an OAuth 2.0 Access Token) is allowed +to modify that blog post. + +![The full ORY Ecosystem](./images/ory-ecosystem.png) +If you were to use the full ORY Ecosystem, it would probably look something like this. Keep in mind that any component +shown here can be replaced or removed, depending on your use case. + +Now you know what this ecosystem has to offer you. To get some more information on the services, read the developer guide by +selecting the software of your choice from the navigation on the left! + +Contact us at [hi@ory.sh](hi@ory.sh) if you need consulting with your specific project. + +## Examples + +The [ory/examples](https://github.com/ory/examples) repository contains numerous examples of setting up these services +individually, and together. Check them out to get a real-world view of this ecosystem! diff --git a/_legacy/_master/guides/SUMMARY.md b/_legacy/_master/guides/SUMMARY.md new file mode 100644 index 000000000..83fb908f3 --- /dev/null +++ b/_legacy/_master/guides/SUMMARY.md @@ -0,0 +1,62 @@ +## Overview + +* [ORY Developer Guide](README.md) + +## updating + +* [Performing Software Updates](updating/README.md) + +## ORY Hydra + +* [Introduction](hydra/README.md) + * [5 Minute Tutorial](hydra/1-tutorial/README.md) + * [Dependencies & Environment](hydra/2-environment/README.md) + * [Securing ORY Hydra](hydra/2-environment/1-securing-ory-hydra.md) + * [Concepts and Theory](hydra/3-overview/README.md) + * [OAuth 2.0 & OpenID Connect](hydra/3-overview/1-oauth2.md) + * [JSON Web Keys (JWK)](hydra/3-overview/2-jwk.md) + * [Limitations](hydra/3-overview/3-limitations.md) + * [Install, Configure and Run ORY Hydra](hydra/4-install/README.md) + * [Security Overview](hydra/5-security/README.md) + * [How To](hydra/6-how-to/README.md) + * [ORY Hydra in Production](hydra/6-how-to/1-production.md) + * [Integrating ORY Hydra](hydra/6-how-to/2-architecture.md) + * [Advanced Topics](hydra/6-how-to/3-advanced.md) + * [Debugging](hydra/6-how-to/4-debug.md) + * [SDK](hydra/7-sdk/README.md) + * [Go SDK](hydra/7-sdk/1-go.md) + * [JavaScript SDK](hydra/7-sdk/2-js.md) + * [PHP SDK](hydra/7-sdk/3-php.md) + * [FAQ](hydra/8-faq/README.md) + * [Appendix](hydra/9-appendix/README.md) + +## ORY Oathkeeper + +* [Introduction](oathkeeper/README.md) + * [Quickstart](oathkeeper/1-overview/README.md) + * [Access Rules](oathkeeper/1-overview/1-rules.md) + * [Installing ORY Oathkeeper](oathkeeper/1-overview/2-installation.md) + * [Configuring and Running ORY Oathkeeper](oathkeeper/1-overview/3-configuration.md) + * [Deployment](oathkeeper/1-overview/4-deployment.md) + * [Proxy & Judge](oathkeeper/1-overview/5-proxy-judge.md) + +## ORY Keto + +* [Introduction](keto/README.md) + * [Quickstart](keto/1-overview/README.md) + * [Access Control Policies](keto/1-overview/1-policies.md) + * [Access Control: The Warden API](keto/1-overview/2-warden.md) + * [Roles](keto/1-overview/3-roles.md) + * [Best Practices](keto/1-overview/4-best-practices.md) + * [Conditions and Context](keto/1-overview/5-conditions.md) + * [Installing ORY Keto](keto/1-overview/6-installation.md) + * [Configuring and Running ORY Keto](keto/1-overview/7-configuration.md) + +## Performance Benchmarks + +* [Performance Benchmarks](performance/README.md) + * [ORY Hydra Performance Benchmarks](performance/1-hydra.md) + +## Telemetry + +* [Telemetry](telemetry/README.md) \ No newline at end of file diff --git a/guides/_layouts/website/page.html b/_legacy/_master/guides/_layouts/website/page.html similarity index 100% rename from guides/_layouts/website/page.html rename to _legacy/_master/guides/_layouts/website/page.html diff --git a/_legacy/_master/guides/book.json b/_legacy/_master/guides/book.json new file mode 100644 index 000000000..7df4e5c7f --- /dev/null +++ b/_legacy/_master/guides/book.json @@ -0,0 +1,84 @@ +{ + "author": "Aeneas Rekkas", + "gitbook": ">=3.2.3", + "plugins": [ + "edit-link", + "auto-summary-ory", + "etoc", + "ga", + "sitemap-basepath", + "versions-ory", + "youtube" + ], + "pluginsConfig": { + "auto-summary": { + "parts": { + "README.md": "Overview", + "updating/*.md": "updating", + "hydra/**/*.md": "ORY Hydra", + "oathkeeper/**/*.md": "ORY Oathkeeper", + "keto/**/*.md": "ORY Keto", + "performance/*.md": "Performance Benchmarks", + "telemetry/*.md": "Telemetry" + } + }, + "edit-link": { + "base": "https://github.com/ory/docs/tree/master/guides", + "label": "Edit this page" + }, + "etoc": { + "h2lb": 2, + "mindepth": 2, + "maxdepth": 5, + "notoc": false + }, + "ga": { + "token": "UA-71865250-1" + }, + "sitemap-basepath": { + "hostname": "https://www.ory.sh/", + "basePath": "/docs/guides/" + }, + "versions": { + "gitbookConfigURL": "https://raw.githubusercontent.com/ory/docs/master/guides/book.json", + "options": [ + { + "value": "https://www.ory.sh/docs/guides/latest", + "text": "latest (stable)" + }, + { + "value": "https://www.ory.sh/docs/guides/v1.0.0-beta.8", + "text": "v1.0.0-beta.8" + }, + { + "value": "https://www.ory.sh/docs/guides/v1.0.0-beta.7", + "text": "v1.0.0-beta.7" + }, + { + "value": "https://www.ory.sh/docs/guides/v1.0.0-beta.5", + "text": "v1.0.0-beta.5" + }, + { + "value": "https://www.ory.sh/docs/guides/v1.0.0-beta.4", + "text": "v1.0.0-beta.4" + }, + { + "value": "https://www.ory.sh/docs/guides/v1.0.0-beta.3", + "text": "v1.0.0-beta.3" + }, + { + "value": "https://www.ory.sh/docs/guides/v1.0.0-beta.2", + "text": "v1.0.0-beta.2" + }, + { + "value": "https://www.ory.sh/docs/guides/v0.11.12", + "text": "0.11.12" + }, + { + "value": "https://www.ory.sh/docs/guides/master", + "text": "master (unstable)" + } + ] + } + } +} diff --git a/guides/hydra/1-tutorial/README.md b/_legacy/_master/guides/hydra/1-tutorial/README.md similarity index 100% rename from guides/hydra/1-tutorial/README.md rename to _legacy/_master/guides/hydra/1-tutorial/README.md diff --git a/guides/hydra/2-environment/1-securing-ory-hydra.md b/_legacy/_master/guides/hydra/2-environment/1-securing-ory-hydra.md similarity index 100% rename from guides/hydra/2-environment/1-securing-ory-hydra.md rename to _legacy/_master/guides/hydra/2-environment/1-securing-ory-hydra.md diff --git a/guides/hydra/2-environment/README.md b/_legacy/_master/guides/hydra/2-environment/README.md similarity index 100% rename from guides/hydra/2-environment/README.md rename to _legacy/_master/guides/hydra/2-environment/README.md diff --git a/guides/hydra/3-overview/1-oauth2.md b/_legacy/_master/guides/hydra/3-overview/1-oauth2.md similarity index 100% rename from guides/hydra/3-overview/1-oauth2.md rename to _legacy/_master/guides/hydra/3-overview/1-oauth2.md diff --git a/guides/hydra/3-overview/2-jwk.md b/_legacy/_master/guides/hydra/3-overview/2-jwk.md similarity index 100% rename from guides/hydra/3-overview/2-jwk.md rename to _legacy/_master/guides/hydra/3-overview/2-jwk.md diff --git a/guides/hydra/3-overview/3-limitations.md b/_legacy/_master/guides/hydra/3-overview/3-limitations.md similarity index 100% rename from guides/hydra/3-overview/3-limitations.md rename to _legacy/_master/guides/hydra/3-overview/3-limitations.md diff --git a/guides/hydra/3-overview/README.md b/_legacy/_master/guides/hydra/3-overview/README.md similarity index 100% rename from guides/hydra/3-overview/README.md rename to _legacy/_master/guides/hydra/3-overview/README.md diff --git a/_legacy/_master/guides/hydra/4-install/README.md b/_legacy/_master/guides/hydra/4-install/README.md new file mode 100644 index 000000000..342ae16a6 --- /dev/null +++ b/_legacy/_master/guides/hydra/4-install/README.md @@ -0,0 +1,323 @@ +# Install, Configure and Run ORY Hydra + +The goal of this chapter is to introduce you to a fully functional set up that includes ORY Hydra as well as our +User Login & Consent Provider reference implementation. + + + +The goal of this section is to familiarize you with the specifics of setting up ORY Hydra in your environment. +Before starting with this section, please check out the [tutorial](../1-tutorial). It will teach you the most important flows +and settings for Hydra. + +This guide will: + +1. Download and run a PostgreSQL container in Docker. +2. Download and run ORY Hydra in Docker. +3. Download and run our reference User Login & Consent Provider. +4. Create an OAuth 2.0 Client to perform the OAuth 2.0 Authorize Code Flow. +5. Perform the OAuth 2.0 Authorize Code flow. + +Before starting with this guide, please install the most recent version of [Docker](https://www.docker.com/community-edition#/download). +While docker is not required for running ORY Hydra, we recommend using it for this tutorial as it will greatly reduce +the complexity of setting up a database on your system without virtualization, installing Go, and compiling ORY Hydra. + +## Create a Network + +Before we can start, a network must be created which we will attach all our Docker containers to. That way, the containers +can talk to one another. + +``` +$ docker network create hydraguide +``` + +## Start a PostgreSQL Container + +For the purpose of this tutorial, we will use PostgreSQL as a database. As you probably already know, don't run databases in Docker in production! +For the sake of this tutorial however, let's use Docker to quickly deploy the database. + +``` +$ docker run \ + --network hydraguide \ + --name ory-hydra-example--postgres \ + -e POSTGRES_USER=hydra \ + -e POSTGRES_PASSWORD=secret \ + -e POSTGRES_DB=hydra \ + -d postgres:9.6 +``` + +This command wil start a postgres instance with name `ory-hydra-example--postgres`, set up a database called `hydra` +and create a user `hydra` with password `secret`. + +## Install and run ORY Hydra + +We highly recommend using Docker to run Hydra, as installing, configuring and running Hydra is easiest with Docker. +ORY Hydra is available on [Docker Hub](https://hub.docker.com/r/oryd/hydra/). + +``` +# The system secret can only be set against a fresh database. Key rotation is currently not supported. This +# secret is used to encrypt the database and needs to be set to the same value every time the process (re-)starts. +# You can use /dev/urandom to generate a secret. But make sure that the secret must be the same anytime you define it. +# You could, for example, store the value somewhere. +$ export SYSTEM_SECRET=$(export LC_CTYPE=C; cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) +# +# Alternatively you can obviously just set a secret: +# $ export SYSTEM_SECRET=this_needs_to_be_the_same_always_and_also_very_$3cuR3-._ + +# The database url points us at the postgres instance. This could also be an ephermal in-memory database (`export DATABASE_URL=memory`) +# or a MySQL URI. +$ export DATABASE_URL=postgres://hydra:secret@ory-hydra-example--postgres:5432/hydra?sslmode=disable + +# Before starting, let's pull the latest ORY Hydra tag from docker. +$ docker pull oryd/hydra:v1.0.0-beta.8 + +# This command will show you all the environment variables that you can set. Read this carefully. +# It is the equivalent to `hydra help serve`. +$ docker run -it --rm --entrypoint hydra oryd/hydra:v1.0.0-beta.8 help serve + +Starts all HTTP/2 APIs and connects to a database backend. +[...] + +# ORY Hydra does not do magic, it requires conscious decisions, for example running SQL migrations which is required +# when installing a new version of ORY Hydra, or upgrading an existing installation. +# It is the equivalent to `hydra migrate sql postgres://hydra:secret@ory-hydra-example--postgres:5432/hydra?sslmode=disable` +$ docker run -it --rm \ + --network hydraguide \ + oryd/hydra:v1.0.0-beta.8 \ + migrate sql $DATABASE_URL + +Applying `client` SQL migrations... +[...] +Migration successful! + +# Let's run the server (settings explained below): +$ docker run -d \ + --name ory-hydra-example--hydra \ + --network hydraguide \ + -p 9000:4444 \ + -p 9001:4445 \ + -e SYSTEM_SECRET=$SYSTEM_SECRET \ + -e DATABASE_URL=$DATABASE_URL \ + -e OAUTH2_ISSUER_URL=https://localhost:9000/ \ + -e OAUTH2_CONSENT_URL=http://localhost:9020/consent \ + -e OAUTH2_LOGIN_URL=http://localhost:9020/login \ + oryd/hydra:v1.0.0-beta.8 serve all + +# And check if it's running: +$ docker logs ory-hydra-example--hydra + +time="2017-06-29T21:26:26Z" level=info msg="Connecting with postgres://*:*@postgres:5432/hydra?sslmode=disable" +time="2017-06-29T21:26:26Z" level=info msg="Connected to SQL!" +[...] +time="2017-06-29T21:26:34Z" level=info msg="Setting up http server on :4444" +``` + +Let's dive into the various settings: + +* `--network hydraguide` connects this instance to the network and makes it possible to connect to the PostgreSQL database. +* `-p 9000:4444` exposes ORY Hydra's public API on `https://localhost:9000/`. +* `-p 9001:4445` exposes ORY Hydra's administrative API on `https://localhost:9001/`. +* `-e SYSTEM_SECRET=$SYSTEM_SECRET` sets the system secret environment variable **(required)**. +* `-e DATABASE_URL=$DATABASE_URL` sets the database url environment variable **(required)**. +* `-e OAUTH2_ISSUER_URL=https://localhost:9000/` this value must be set to the publicly available URL of ORY Hydra **(required)**. +* `-e OAUTH2_CONSENT_URL=http://localhost:9020/consent` this sets the URL of the consent provider **(required)**. We will set up the service +that handles requests at that URL in the next sections. +* `-e OAUTH2_LOGIN_URL=http://localhost:9020/login` this sets the URL of the login provider **(required)**. We will set up the service +that handles requests at that URL in the next sections. + +Note: In this example we did not define a value for the optional setting `OAUTH2_ERROR_URL`. This URL can be used +to provide an endpoint which will receive error messages from ORY Hydra that should be displayed +to the end user. The URL receives `error` and `error_description` parameters. If this value is not set, +Hydra uses the fallback endpoint `/oauth2/fallbacks/error` and displays a default error message. In order to obtain +a uniform UI, you might want to include such an endpoint in your login or consent provider. + +To confirm that the instance is running properly, [open the health check](https://localhost:9001/health/status). If asked, +accept the self signed certificate in your browser. You should simply see `ok`. + +On start up, ORY Hydra is initializing some values. Let's take a look at the logs: + +``` +$ docker logs ory-hydra-example--hydra +time="2017-06-30T09:06:34Z" level=info msg="Connecting with postgres://*:*@postgres:5432/hydra?sslmode=disable" +time="2017-06-30T09:06:34Z" level=info msg="Connected to SQL!" +time="2017-06-30T09:06:34Z" level=info msg="Key pair for signing hydra.openid.id-token is missing. Creating new one." +time="2017-06-30T09:06:41Z" level=warning msg="No TLS Key / Certificate for HTTPS found. Generating self-signed certificate." +time="2017-06-30T09:06:41Z" level=info msg="Setting up http server on :4444" +``` + +As you can see, the following steps are performed when running ORY Hydra against a fresh database: + +1. If no system secret was given (in our case we provided one), a random one is generated and emitted to the logs. +Note this down, otherwise you won't be able to restart Hydra. +2. Cryptographic keys are generated for the OpenID Connect ID Token, the consent challenge and response, and TLS encryption +using a self-signed certificate, which is why we need to run all commands using `--skip-tls-verify`. + +ORY Hydra can be managed using the Hydra Command Line Interface (CLI), which is using ORY Hydra's REST APIs. To +see the available commands, run: + +``` +$ docker run --rm -it --entrypoint hydra oryd/hydra:v1.0.0-beta.8 help +Hydra is a cloud native high throughput OAuth2 and OpenID Connect provider + +Usage: + hydra [command] + +[...] +``` + +### Install ORY Hydra without Docker + +You can also install ORY Hydra without docker. For the purpose of this tutorial, [please skip this section for now](#configure-ory-hydra), and read +it later. + +#### Download Binaries + +The client and server **binaries are downloadable at the [releases tab](https://github.com/ory/hydra/releases)**. +There is currently no installer available. You have to add the Hydra binary to the PATH environment variable yourself or put +the binary in a location that is already in your `$PATH` (e.g. `/usr/bin`, ...). + +Once installed, you should be able to run: + +``` +$ hydra help + +Hydra is a cloud native high throughput OAuth2 and OpenID Connect provider + +Usage: + hydra [command] + +Available Commands: + clients Manage OAuth2 clients +... +``` + +#### Build from Source + +If you wish to compile ORY Hydra yourself, you need to install and set up [Go 1.10+](https://golang.org/) and add `$GOPATH/bin` +to your `$PATH` as well as [golang/dep](http://github.com/golang/dep). + +The following commands will check out the latest release tag of ORY Hydra and compile it and set up flags so that `hydra version` +works as expected. Please note that this will only work with a linux shell like bash or sh. + +``` +``` +go get -d -u github.com/ory/hydra +cd $(go env GOPATH)/src/github.com/ory/hydra +HYDRA_LATEST=$(git describe --abbrev=0 --tags) +git checkout $HYDRA_LATEST +dep ensure -vendor-only +go install \ + -ldflags "-X github.com/ory/hydra/cmd.Version=$HYDRA_LATEST -X github.com/ory/hydra/cmd.BuildTime=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/hydra/cmd.GitHash=`git rev-parse HEAD`" \ + github.com/ory/hydra +git checkout master +hydra help + +... +``` + +### Setting up the Login & Consent Provider + +The Login Provider and Consent Provider can be two separate web services. We provide a [reference implementation](https://github.com/ory/hydra-login-consent-node) which +combines both features in one app. Here, we will use deploy that app using Docker. + +``` +$ docker pull oryd/hydra-login-consent-node:v1.0.0-beta.8 +$ docker run -d \ + --name ory-hydra-example--consent \ + -p 9020:3000 \ + --network hydraguide \ + -e HYDRA_URL=https://ory-hydra-example--hydra:4445 \ + -e NODE_TLS_REJECT_UNAUTHORIZED=0 \ + oryd/hydra-login-consent-node:v1.0.0-beta.8 + +# Let's check if it's running ok: +$ docker logs ory-hydra-example--consent +``` + +Let's take a look at the arguments: +* `-p 9020:3000` exposes this service at port 9020. If you remember, that's the port of the `OAUTH2_CONSENT_URL` and `OAUTH2_LOGIN_URL` value +from the ORY Hydra docker container (`OAUTH2_CONSENT_URL=http://localhost:9020/consent`, `OAUTH2_LOGIN_URL=http://localhost:9020/login`). +* `HYDRA_URL=http://hydra:4445` point to the ORY Hydra Administrative API. +* `NODE_TLS_REJECT_UNAUTHORIZED=0` disables TLS verification, because we are using self-signed certificates. + +## Perform OAuth 2.0 Flow + +Great! Our infrastructure is all set up! Next it's time to perform the OAuth 2.0 Authorize Code flow. For that purpose, +the ORY Hydra CLI has a feature that sets up an OAuth 2.0 Consumer and an OAuth 2.0 callback URL. Typically, this would +be a third-party application that requests access to a user's resources on your servers - for example a Facebook App you wrote +that backs up a user's photos and thus requires read access to the user's photos. + +Before we go ahead, the OAuth 2.0 Client that performs the request has to be set up. Let's call the client `facebook-photo-backup`. +We have to specify which OAuth 2.0 Grant Types, OAuth 2.0 Scope, OAuth 2.0 Response Types, and Callback URLs the client may request: + +``` +$ docker run --rm -it \ + -e HYDRA_ADMIN_URL=https://ory-hydra-example--hydra:4445 \ + --network hydraguide \ + oryd/hydra:v1.0.0-beta.8 \ + clients create --skip-tls-verify \ + --id facebook-photo-backup \ + --secret some-secret \ + --grant-types authorization_code,refresh_token,client_credentials,implicit \ + --response-types token,code,id_token \ + --scope openid,offline,photos.read \ + --callbacks http://127.0.0.1:9010/callback + +Client ID: facebook-photo-backup +Client Secret: some-secret +``` + +Let's dive into some of the arguments: +* `--skip-tls-verify` is supported by all management commands (create/delete/update/... OAuth 2.0 Client, JSON Web Key, ...) + and tells the CLI to trust any certificate authority - even self-signed ones. We need this flag because the server + uses a self-signed certificate. In production deployments, you would use a certificate signed by a trusted CA. +* `--grant-types authorize_code,refresh_token,client_credentials,implicit` we want to be able to perform all of these +OAuth 2.0 flows. +* `--response-types token,code,id_token` allows us to receive authorize codes, access and refresh tokens, and +OpenID Connect ID Tokens. +* `--scope openid,offline,fotos.read` allows the client to request various permissions: + * `openid` allows the client to perform the OpenID Connect flow and request an OpenID Connect ID Token. + * `offline` allows the client to request a refresh token. Because we want to continuously backup photos, the app must be + able to refresh expired access tokens. This scope allows that. + * `photos.read` this is an imaginary scope that is not handled by ORY Hydra but serves the purpose of making it clear that + we could request read access to a user's photos. You can obviously omit this scope or use your own scope. +* `--callbacks http://localhost:9010/callback` allows the client to request this redirect uri. + +Perfect, let's perform an exemplary OAuth 2.0 Authorize Code Flow! To make this easy, the ORY Hydra CLI provides +a helper command called `hydra token user`. Just imagine this being, for example, passport.js that is generating +an auth code url, redirecting the browser to it, and then exchanging the authorize code for an access token. The +same thing happens with this command: + +``` +$ docker run --rm -it \ + --network hydraguide \ + -p 9010:9010 \ + oryd/hydra:v1.0.0-beta.8 \ + token user --skip-tls-verify \ + --port 9010 \ + --auth-url https://localhost:9000/oauth2/auth \ + --token-url https://ory-hydra-example--hydra:4444/oauth2/token \ + --client-id facebook-photo-backup \ + --client-secret some-secret \ + --scope openid,offline,photos.read + +Setting up callback listener on http://localhost:9010/callback +Press ctrl + c on Linux / Windows or cmd + c on OSX to end the process. +If your browser does not open automatically, navigate to: + + https://localhost:9010/ +``` + +open the link, as prompted, in your browser, and follow the steps shown there. You might encounter a screen like the following +one: + +![Insecure connection](../images/insecure-connection.jpg) + +This happens because we run ORY Hydra with a self-signed TLS certificate. In production deployments, you would probably +use a certificate signed by a trusted CA and not see this screen. + +When you see this screen, click on "Advanced" and "Add Exception" to continue. In some browsers, this might work differently, +but it's always possible to proceed. + +When completed, you should land at a screen that looks like this one: + +![OAuth 2.0 result](../images/install-result.png) diff --git a/guides/hydra/5-security/README.md b/_legacy/_master/guides/hydra/5-security/README.md similarity index 100% rename from guides/hydra/5-security/README.md rename to _legacy/_master/guides/hydra/5-security/README.md diff --git a/guides/hydra/6-how-to/1-production.md b/_legacy/_master/guides/hydra/6-how-to/1-production.md similarity index 100% rename from guides/hydra/6-how-to/1-production.md rename to _legacy/_master/guides/hydra/6-how-to/1-production.md diff --git a/guides/hydra/6-how-to/2-architecture.md b/_legacy/_master/guides/hydra/6-how-to/2-architecture.md similarity index 100% rename from guides/hydra/6-how-to/2-architecture.md rename to _legacy/_master/guides/hydra/6-how-to/2-architecture.md diff --git a/_legacy/_master/guides/hydra/6-how-to/3-advanced.md b/_legacy/_master/guides/hydra/6-how-to/3-advanced.md new file mode 100644 index 000000000..1f4b7decb --- /dev/null +++ b/_legacy/_master/guides/hydra/6-how-to/3-advanced.md @@ -0,0 +1,195 @@ +# Advanced Topics + +This guide aims to help setting up a production system with ORY Hydra. + + + +## Mobile & Browser (SPA) Authorization + +We have an [excellent blog post](https://www.ory.sh/oauth2-for-mobile-app-spa-browser) on this topic. Read it now! + +### Creating a public OAuth 2.0 Client + +You can create a public OAuth 2.0 Client (e.g. for the authorize code + PKCE or implicit flow) with the CLI + +``` +hydra clients create --endpoint http://ory-hydra-admin-api --token-endpoint-auth-method none +``` + +or by setting in the HTTP API JSON body when POSTing to `/clients`: + +``` +{ + "client_id": "...", + "token_endpoint_auth_method": "none" +} +``` + +Be aware that when making requests to `/oauth2/token` with a public OAuth 2.0 Client, you can not authenticate with the +HTTP Basic Authorization but must include the `client_id` in the POST body. + +## Key rotation + +Key rotation is simple with ORY Hydra. You can rotate OpenID Connect ID Token and OAuth 2.0 Access Tokens (JSON Web Token) +keys with one simple command. + +ORY Hydra takes the latest key from the key store to sign JSON Web Tokens. All public keys will be shown at +`http://ory-hydra-public-api/.well-known/jwks.json`. + +### OpenID Connect ID Token + +``` +hydra keys create --endpoint=http://ory-hydra-admin-api/ hydra.openid.id-token -a RS256 +``` + +### OAuth 2.0 Access Tokens (JSON Web Token) + +``` +hydra keys create --endpoint=http://ory-hydra-admin-api/ hydra.jwt.access-token -a RS256 +``` + +## OAuth 2.0 + +### JSON Web Tokens + +ORY Hydra supports JSON Web Tokens as Access Tokens. We *discourage you from using this feature for multiple reasons:* + +1. It is very new and has not been battle-tested. +2. We believe that JSON Web Tokens can lead to poor security practices. +3. Using this feature disables other features, like the pairwise Subject Identifier Algorithm. + +If you still want to use this strategy you can do so by setting environment variable `OAUTH2_ACCESS_TOKEN_STRATEGY=jwt`. + +Be aware that only access tokens are formatted as JSON Web Tokens. Refresh tokens are not impacted by this strategy. +By performing OAuth 2.0 Token Introspection you can check if the token is still valid. If a token is revoked or otherwise +blacklisted, the OAuth 2.0 Token Introspection will return `{ "active": false }`. This is useful when you do not want +to rely only on the token's expiry. + +#### JSON Web Token Validation + +You can validate JSON Web Tokens issued by ORY Hydra by pointing your `jwt` library (e.g. [node-jwks-rsa](https://github.com/auth0/node-jwks-rsa)) +to `http://ory-hydra-public-api/.well-known/jwks.json`. All necessary keys are available there. + +### OAuth 2.0 Client Authentication with RSA private/public keypairs + +ORY Hydra supports OAuth 2.0 Client Authentication with RSA private/public keypairs. This authentication method +replaces the classic HTTP Basic Authorization and HTTP POST Authorization schemes. Instead of sending the `client_id` +and `client_secret`, you authenticate the client with a signed JSON Web Token. + +To enable this feature for a specific OAuth 2.0 Client, you must set `token_endpoint_auth_method` to `private_key_jwt` +and register the public key of the RSA signing key either using the `jwks_uri` or `jwks` fields of the client. + +When authenticating the client at the token endpoint, you generate and sign (with the RSA private key) a JSON Web Token +with the following claims: + +* `iss`: REQUIRED. Issuer. This MUST contain the client_id of the OAuth Client. +* `sub`: REQUIRED. Subject. This MUST contain the client_id of the OAuth Client. +* `aud`: REQUIRED. Audience. The aud (audience) Claim. Value that identifies the Authorization Server (ORY Hydra) as an +intended audience. The Authorization Server MUST verify that it is an intended audience for the token. +The Audience SHOULD be the URL of the Authorization Server's Token Endpoint. +* `jti`: REQUIRED. JWT ID. A unique identifier for the token, which can be used to prevent reuse of the token. +These tokens MUST only be used once, unless conditions for reuse were negotiated between the parties; any such +negotiation is beyond the scope of this specification. +* `exp`: REQUIRED. Expiration time on or after which the ID Token MUST NOT be accepted for processing. +* `iat`: OPTIONAL. Time at which the JWT was issued. + +When making a request to the `/oauth2/token` endpoint, you include `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer` +and `client_assertion=` in the request body: + +``` +POST /oauth2/token HTTP/1.1 +Host: my-hydra.com +Content-Type: application/x-www-form-urlencoded + +grant_type=authorization_code& +code=i1WsRn1uB1& +client_id=s6BhdRkqt3& +client_assertion_type= +urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer& +client_assertion=PHNhbWxwOl ... ZT +``` + +Here's what a client with a `jwks` looks like: + +``` +{ + "client_id": "client-jwks", + "jwks": { + "keys": [ + { + "kty": "RSA", + "n": "jL7h5wc-yeMUsHGJHc0xe9SbTdaLKXMHvcIHQck20Ji7SvrHPdTDQTvZtTDS_wJYbeShcCrliHvbJRSZhtEe0mPJpyWg3O_HkKy6_SyHepLK-_BR7HfcXYB6pVJCG3BW-lVMY7gl5sULFA74kNZH50h8hdmyWC9JgOHn0n3YLdaxSWlhctuwNPSwqwzY4qtN7_CZub81SXWpKiwj4UpyB10b8rM8qn35FS1hfsaFCVi0gQpd4vFDgFyqqpmiwq8oMr8RZ2mf0NMKCP3RXnMhy9Yq8O7lgG2t6g1g9noWbzZDUZNc54tv4WGFJ_rJZRz0jE_GR6v5sdqsDTdjFquPlQ", + "e": "AQAB" + } + ] + }, + "token_endpoint_auth_method": "private_key_jwt" +} +``` + +And with `jwks_uri`: + +``` +{ + "client_id": "client-jwks-uri", + "jwks_uri": "http://path-to-my-public/keys.json", + "token_endpoint_auth_method": "private_key_jwt" +} +``` + +The `jwks_uri` must return a JSON object containing the public keys associated with the OAuth 2.0 Client: + +``` +{ + "keys": [ + { + "kty": "RSA", + "n": "jL7h5wc-yeMUsHGJHc0xe9SbTdaLKXMHvcIHQck20Ji7SvrHPdTDQTvZtTDS_wJYbeShcCrliHvbJRSZhtEe0mPJpyWg3O_HkKy6_SyHepLK-_BR7HfcXYB6pVJCG3BW-lVMY7gl5sULFA74kNZH50h8hdmyWC9JgOHn0n3YLdaxSWlhctuwNPSwqwzY4qtN7_CZub81SXWpKiwj4UpyB10b8rM8qn35FS1hfsaFCVi0gQpd4vFDgFyqqpmiwq8oMr8RZ2mf0NMKCP3RXnMhy9Yq8O7lgG2t6g1g9noWbzZDUZNc54tv4WGFJ_rJZRz0jE_GR6v5sdqsDTdjFquPlQ", + "e": "AQAB" + } + ] +} +``` + +## OpenID Connect + +### Subject Identifier Algorithms + +Hydra supports two [Subject Identifier Algorithms](http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes): + +* `public`: This provides the same `sub` (subject) value to all Clients (default). +* `pairwise`: This provides a different `sub` value to each Client, so as not to enable Clients to +correlate the End-User's activities without permission. + +You can enable either one or both algorithms using the `OIDC_SUBJECT_TYPES_SUPPORTED` environment variable: + +* `export IDC_SUBJECT_TYPES_SUPPORTED=public` (default) +* `export IDC_SUBJECT_TYPES_SUPPORTED=pairwise` +* `export IDC_SUBJECT_TYPES_SUPPORTED=public,pairwise` + +If `pairwise` is enabled, you must also set the environment variable `OIDC_SUBJECT_TYPE_PAIRWISE_SALT`. The salt +is used to obfuscate the `sub` value. + +**This value should not be changed once set in production. Changing it will cause all client applications +to receive new user IDs from ORY Hydra which will lead to serious complications with authentication on their side!** + +Each OAuth 2.0 Client has a configuration field `subject_type`. The value of that `subject_type` is either `public` or +`pairwise`. If the mode is enabled by `IDC_SUBJECT_TYPES_SUPPORTED`, then ORY Hydra will choose the right strategy automatically. + +While ORY Hydra handles `sub` obfuscation out of the box, you may also override this value with your own obfuscated +`sub` value by setting `force_subject_identifier` when accepting the login challenge in your user login app. + +## System Secret Rotation + +We advise to rotate the system secret from time to time. The system secret is used to sign and validate OAuth 2.0 Access +and Refresh Tokens and to encrypt JSON Web Keys in the store. If you use the JWT strategy for OAuth 2.0 Access Tokens, +the system secret has no effect on these. + +To rotate the system secret (only possible with SQL at the moment), follow this guide: + +1. Shutdown all ORY Hydra instances +2. Run `OLD_SYSTEM_SECRET=foo NEW_SYSTEM_SECRET=bar hydra migrate secret db://url/...` +3. Decide if access/refresh tokens signed with the old key should still be valid. + * If yes, set `ROTATED_SYSTEM_SECRET` to the old secret before starting `hydra serve ...`, and `SYSTEM_SECRET` to the new one. + * If not, set only `SYSTEM_SECRET` to the new secret before running `hydra serve ...`. +4. Restart ORY Hydra instances. diff --git a/guides/hydra/6-how-to/4-debug.md b/_legacy/_master/guides/hydra/6-how-to/4-debug.md similarity index 100% rename from guides/hydra/6-how-to/4-debug.md rename to _legacy/_master/guides/hydra/6-how-to/4-debug.md diff --git a/guides/hydra/6-how-to/README.md b/_legacy/_master/guides/hydra/6-how-to/README.md similarity index 100% rename from guides/hydra/6-how-to/README.md rename to _legacy/_master/guides/hydra/6-how-to/README.md diff --git a/guides/hydra/7-sdk/1-go.md b/_legacy/_master/guides/hydra/7-sdk/1-go.md similarity index 100% rename from guides/hydra/7-sdk/1-go.md rename to _legacy/_master/guides/hydra/7-sdk/1-go.md diff --git a/guides/hydra/7-sdk/2-js.md b/_legacy/_master/guides/hydra/7-sdk/2-js.md similarity index 100% rename from guides/hydra/7-sdk/2-js.md rename to _legacy/_master/guides/hydra/7-sdk/2-js.md diff --git a/guides/hydra/7-sdk/3-php.md b/_legacy/_master/guides/hydra/7-sdk/3-php.md similarity index 100% rename from guides/hydra/7-sdk/3-php.md rename to _legacy/_master/guides/hydra/7-sdk/3-php.md diff --git a/guides/hydra/7-sdk/README.md b/_legacy/_master/guides/hydra/7-sdk/README.md similarity index 100% rename from guides/hydra/7-sdk/README.md rename to _legacy/_master/guides/hydra/7-sdk/README.md diff --git a/_legacy/_master/guides/hydra/8-faq/README.md b/_legacy/_master/guides/hydra/8-faq/README.md new file mode 100644 index 000000000..d18839712 --- /dev/null +++ b/_legacy/_master/guides/hydra/8-faq/README.md @@ -0,0 +1,201 @@ +# FAQ + +This file keeps track of questions and discussions from Gitter and general help with various issues. + + + +## How can I control SQL connection limits? + +You can configure SQL connection limits by appending parameters `max_conns`, `max_idle_conns`, or `max_conn_lifetime` +to the DSN: `postgres://foo:bar@host:port/database?max_conns=12`. + +## Why is the Resource Owner Password Credentials grant not supported? + +The following is a copy of the original [comment on GitHub](https://github.com/ory/hydra/pull/297#issuecomment-294282671): + +I took a long time for this issue, primarily because I felt very uncomfortable implementing it. The ROCP grant is something from the "dark ages" of OAuth2 and there are suitable replacements for mobile clients, such as public oauth2 clients, which are supported by Hydra: https://tools.ietf.org/html/draft-ietf-oauth-native-apps-09 + +The OAuth2 Thread Model explicitly states that the ROPC grant is commonly used in legacy/migration scenarios, and + +> This grant type has higher + risk because it maintains the UID/password anti-pattern. + Additionally, because the user does not have control over the + authorization process, clients using this grant type are not limited by scope but instead have potentially the same capabilities as the + user themselves. As there is no authorization step, the ability to + offer token revocation is bypassed. + +> Because passwords are often used for more than 1 service, this + anti-pattern may also put at risk whatever else is accessible with + the supplied credential. Additionally, any easily derived equivalent + (e.g., joe@example.com and joe@example.net) might easily allow + someone to guess that the same password can be used elsewhere. + +> Impact: The resource server can only differentiate scope based on the + access token being associated with a particular client. The client + could also acquire long-lived tokens and pass them up to an + attacker's web service for further abuse. The client, eavesdroppers, + or endpoints could eavesdrop the user id and password. + +> o Except for migration reasons, minimize use of this grant type. + +- [source](https://tools.ietf.org/html/rfc6819#section-4.4.3) + +Thus, I decided to not implement the ROPC grant in Hydra. Over time, I will add documentation how to deal with mobile scenarios and similar. + +## Should I use OAuth2 tokens for authentication? + +OAuth2 tokens are like money. It allows you to buy stuff, but the cashier does not really care if the money is +yours or if you stole it, as long as it's valid money. Depending on what you understand as authentication, this is a yes and no answer: + +* **Yes:** You can use access tokens to find out which user ("subject") is performing an action in a resource provider (blog article service, shopping basket, ...). +Coming back to the money example: *You*, the subject, receives a cappuccino from the vendor (resource provider) in exchange for money (access token). +* **No:** Never use access tokens for logging people in, for example `http://myapp.com/login?access_token=...`. +Coming back to the money example: The police officer ("authentication server") will not accept money ("access token") as a proof of identity ("it's really you"). Unless he is corrupt ("vulnerable"), of course. + +In the second example ("authentication server"), you must use OpenID Connect ID Tokens. + +## How to deal with mobile apps? + +Read [this article](https://www.ory.sh/oauth2-for-mobile-app-spa-browser). + +## How should I run migrations? + +Since ORY Hydra 0.8.0, migrations are no longer run automatically on boot. This is required in production environments, +because: + +1. Although SQL migrations are tested, migrating schemas can cause data loss and should only be done consciously with +prior back ups. +2. Running a production system with a user that has right such as ALTER TABLE is a security anti-pattern. + +Thus, to initialize the database schemas, it is required to run `hydra migrate sql driver://user:password@host:port/db` before running +`hydra host`. + +## What does the installation process look like? + +1. Run `hydra migrate sql ...` on a host close to the database (e.g. a virtual machine with access to the SQL instance). + +## What does a migration process look like? + +1. Make sure a database update is required by checking the release notes. +2. Make a back up of the database. +3. Run the migration script on a host close to the database (e.g. a virtual machine with access to the SQL instance). +Schemas are usually backwards compatible, so instances running previous versions of ORY Hydra should keep working fine. +If backwards compatibility is not given, this will be addressed in the patch notes. +4. Upgrade all ORY Hydra instances. + +## How can I do this in docker? + +Many deployments of ORY Hydra use Docker. Although several options are available, we advise to extend the ORY Hydra Docker +image + +**Dockerfile** +``` +FROM oryd/hydra:tag + +ENTRYPOINT /go/bin/hydra migrate sql $DATABASE_URL +``` + +and run it in your infrastructure once. + +Additionally, *but not recommended*, it is possible to override the entry point of the ORY Hydra Docker image using CLI flag +`--entrypoint "hydra migrate sql $DATABASE_URL; hydra host"` or with `entrypoint: hydra migrate sql $DATABASE_URL; hydra host` +set in your docker compose config. + +## Can I set the log level to warn, error, debug, ...? + +Yes, you can do so by setting the environment variable `LOG_LEVEL=`. There are various levels supported: + +* debug +* info +* warn +* error +* fatal +* panic + +## How can I import TLS certificates? + +You can import TLS certificates when running `hydra host`. This can be done by setting the following environment variables: + +**Read from file** +- `HTTPS_TLS_CERT_PATH`: The path to the TLS certificate (pem encoded). +- `HTTPS_TLS_KEY_PATH`: The path to the TLS private key (pem encoded). + +**Embedded** +- `HTTPS_TLS_CERT`: A pem encoded TLS certificate passed as string. Can be used instead of TLS_CERT_PATH. +- `HTTPS_TLS_KEY`: A pem encoded TLS key passed as string. Can be used instead of TLS_KEY_PATH. + +Or by specifying the following flags: + +``` +--https-tls-cert-path string Path to the certificate file for HTTP/2 over TLS (https). You can set HTTPS_TLS_KEY_PATH or HTTPS_TLS_KEY instead. +--https-tls-key-path string Path to the key file for HTTP/2 over TLS (https). You can set HTTPS_TLS_KEY_PATH or HTTPS_TLS_KEY instead. +``` + +## Is there an HTTP API Documentation? + +Yes, it is available at [Apiary](http://docs.hydra13.apiary.io/). + +## How can I disable HTTPS for testing? + +You can do so by running `hydra host --dangerous-force-http`. + +## MySQL gives `unsupported Scan, storing driver.Value type []uint8 into type *time.Time` + +> did a quick test to get mysql running, but run into migrate sql issue - seems mysql related +An error occurred while running the migrations: Could not apply ladon SQL migrations: Could not migrate sql schema, applied 0 migrations: sql: Scan error on column index 0: unsupported Scan, storing driver.Value type []uint8 into type *time.Time +is this a known bug ? or any specific mysql version which is required (running 5.7) ? + +``` +$ hydra help host +... + - MySQL: If DATABASE_URL is a DSN starting with mysql:// MySQL will be used as storage backend. + Example: DATABASE_URL=mysql://user:password@tcp(host:123)/database?parseTime=true + + Be aware that the ?parseTime=true parameter is mandatory, or timestamps will not work. +... +``` + +## The docker image exits immediately + +Check the logs using `docker logs `. + +## Insufficient Entropy + +> Hey there , I am getting this error when I try request an access token "The request used a security parameter (e.g., anti-replay, anti-csrf) with insufficient entropy (minimum of 8 characters)" + +> Kareem Diaa @kimooz Jun 07 16:41 +Hey there , I am getting this error when I try request an access token "The request used a security parameter (e.g., anti-replay, anti-csrf) with insufficient entropy (minimum of 8 characters)" + +> Aeneas @arekkas Jun 07 16:41 +@kimooz make sure state and nonce are set in your auth code url (http://hydra/oauth2/auth?client_id=...&nonce=THIS_NEEDS_TO_BE_SET&state=THIS_ALSO_NEEDS_TO_BE_SET + +## I get compile errors! + +> I would try deleting the vendor dir and glide’s files and try glide init again or clear Glide’s global cache. + +> follow the steps in the readme https://github.com/ory/hydra#building-from-source + +## Is JWT supported? + +> Mufid @mufid 03:29 +> Could Hydra's Access Token be a JWT? So that my resource server does not need to call Introspection API for each request. + +> Mufid @mufid 03:39 +Yes, the access token looks like JWT, but i am unable to decode it. Here is my example token form Hydra: LpxuGoqWy7lYp9N0Cea8mEGR6IHhyr37jxZXRHqSjRM.nU-jMnAJ7dUKQPjWF4QBEL9OQWVU8zj_ElhrT-FQrWw (JWT Tokens should have 2 dots (3 segments), so this is not a valid JWT) + +> Mufid @mufid 03:56 +*form --> from, typo, sorry. +> Aeneas @arekkas 11:50 +@mufid JWT is not supported at the moment, we might add it, but not as part of the hydra community edition + +## Refreshing tokens + +> Kareem Diaa @kimooz 15:48 +One last question if you don't mind +from your experience do you think that saving the user access token in a session and validating it from the client on ever refresh does that make sense or not? +using the introspect endpoint + +> Aeneas @arekkas 15:51 +nah, simply write your http calls in a way that if a 401 or 403 occurrs, the token is refreshed +that's the easiest +and cleanest diff --git a/guides/hydra/9-appendix/README.md b/_legacy/_master/guides/hydra/9-appendix/README.md similarity index 100% rename from guides/hydra/9-appendix/README.md rename to _legacy/_master/guides/hydra/9-appendix/README.md diff --git a/guides/hydra/README.md b/_legacy/_master/guides/hydra/README.md similarity index 100% rename from guides/hydra/README.md rename to _legacy/_master/guides/hydra/README.md diff --git a/guides/hydra/images/abstract_flow.png b/_legacy/_master/guides/hydra/images/abstract_flow.png similarity index 100% rename from guides/hydra/images/abstract_flow.png rename to _legacy/_master/guides/hydra/images/abstract_flow.png diff --git a/guides/hydra/images/consent-flow.svg b/_legacy/_master/guides/hydra/images/consent-flow.svg similarity index 100% rename from guides/hydra/images/consent-flow.svg rename to _legacy/_master/guides/hydra/images/consent-flow.svg diff --git a/guides/hydra/images/consent-state.svg b/_legacy/_master/guides/hydra/images/consent-state.svg similarity index 100% rename from guides/hydra/images/consent-state.svg rename to _legacy/_master/guides/hydra/images/consent-state.svg diff --git a/guides/hydra/images/consent.png b/_legacy/_master/guides/hydra/images/consent.png similarity index 100% rename from guides/hydra/images/consent.png rename to _legacy/_master/guides/hydra/images/consent.png diff --git a/guides/hydra/images/gliffy/hydra-arch.gliffy b/_legacy/_master/guides/hydra/images/gliffy/hydra-arch.gliffy similarity index 100% rename from guides/hydra/images/gliffy/hydra-arch.gliffy rename to _legacy/_master/guides/hydra/images/gliffy/hydra-arch.gliffy diff --git a/guides/hydra/images/gliffy/hydra.gliffy b/_legacy/_master/guides/hydra/images/gliffy/hydra.gliffy similarity index 100% rename from guides/hydra/images/gliffy/hydra.gliffy rename to _legacy/_master/guides/hydra/images/gliffy/hydra.gliffy diff --git a/guides/hydra/images/google.png b/_legacy/_master/guides/hydra/images/google.png similarity index 100% rename from guides/hydra/images/google.png rename to _legacy/_master/guides/hydra/images/google.png diff --git a/guides/hydra/images/google2.png b/_legacy/_master/guides/hydra/images/google2.png similarity index 100% rename from guides/hydra/images/google2.png rename to _legacy/_master/guides/hydra/images/google2.png diff --git a/guides/hydra/images/hydra-arch-warden.png b/_legacy/_master/guides/hydra/images/hydra-arch-warden.png similarity index 100% rename from guides/hydra/images/hydra-arch-warden.png rename to _legacy/_master/guides/hydra/images/hydra-arch-warden.png diff --git a/guides/hydra/images/hydra-authentication.gif b/_legacy/_master/guides/hydra/images/hydra-authentication.gif similarity index 100% rename from guides/hydra/images/hydra-authentication.gif rename to _legacy/_master/guides/hydra/images/hydra-authentication.gif diff --git a/guides/hydra/images/insecure-connection.png b/_legacy/_master/guides/hydra/images/insecure-connection.png similarity index 100% rename from guides/hydra/images/insecure-connection.png rename to _legacy/_master/guides/hydra/images/insecure-connection.png diff --git a/guides/hydra/images/install-result.png b/_legacy/_master/guides/hydra/images/install-result.png similarity index 100% rename from guides/hydra/images/install-result.png rename to _legacy/_master/guides/hydra/images/install-result.png diff --git a/guides/hydra/images/login-consent-flow.png b/_legacy/_master/guides/hydra/images/login-consent-flow.png similarity index 100% rename from guides/hydra/images/login-consent-flow.png rename to _legacy/_master/guides/hydra/images/login-consent-flow.png diff --git a/guides/hydra/images/login-success-a.gif b/_legacy/_master/guides/hydra/images/login-success-a.gif similarity index 100% rename from guides/hydra/images/login-success-a.gif rename to _legacy/_master/guides/hydra/images/login-success-a.gif diff --git a/guides/hydra/images/logo-essential.png b/_legacy/_master/guides/hydra/images/logo-essential.png similarity index 100% rename from guides/hydra/images/logo-essential.png rename to _legacy/_master/guides/hydra/images/logo-essential.png diff --git a/guides/hydra/images/logo.png b/_legacy/_master/guides/hydra/images/logo.png similarity index 100% rename from guides/hydra/images/logo.png rename to _legacy/_master/guides/hydra/images/logo.png diff --git a/guides/hydra/images/oauth2-flow.gif b/_legacy/_master/guides/hydra/images/oauth2-flow.gif similarity index 100% rename from guides/hydra/images/oauth2-flow.gif rename to _legacy/_master/guides/hydra/images/oauth2-flow.gif diff --git a/guides/hydra/images/run-the-example.gif b/_legacy/_master/guides/hydra/images/run-the-example.gif similarity index 100% rename from guides/hydra/images/run-the-example.gif rename to _legacy/_master/guides/hydra/images/run-the-example.gif diff --git a/guides/hydra/images/sample_trace.png b/_legacy/_master/guides/hydra/images/sample_trace.png similarity index 100% rename from guides/hydra/images/sample_trace.png rename to _legacy/_master/guides/hydra/images/sample_trace.png diff --git a/guides/hydra/images/social-login-example.jpg b/_legacy/_master/guides/hydra/images/social-login-example.jpg similarity index 100% rename from guides/hydra/images/social-login-example.jpg rename to _legacy/_master/guides/hydra/images/social-login-example.jpg diff --git a/guides/hydra/images/social-login-example.png b/_legacy/_master/guides/hydra/images/social-login-example.png similarity index 100% rename from guides/hydra/images/social-login-example.png rename to _legacy/_master/guides/hydra/images/social-login-example.png diff --git a/guides/hydra/images/social-login.png b/_legacy/_master/guides/hydra/images/social-login.png similarity index 100% rename from guides/hydra/images/social-login.png rename to _legacy/_master/guides/hydra/images/social-login.png diff --git a/guides/hydra/images/sponsors/auth0.png b/_legacy/_master/guides/hydra/images/sponsors/auth0.png similarity index 100% rename from guides/hydra/images/sponsors/auth0.png rename to _legacy/_master/guides/hydra/images/sponsors/auth0.png diff --git a/guides/hydra/metrics/telemetry-example.json b/_legacy/_master/guides/hydra/metrics/telemetry-example.json similarity index 100% rename from guides/hydra/metrics/telemetry-example.json rename to _legacy/_master/guides/hydra/metrics/telemetry-example.json diff --git a/guides/images/basic-oauth2-system.png b/_legacy/_master/guides/images/basic-oauth2-system.png similarity index 100% rename from guides/images/basic-oauth2-system.png rename to _legacy/_master/guides/images/basic-oauth2-system.png diff --git a/guides/images/ory-ecosystem.png b/_legacy/_master/guides/images/ory-ecosystem.png similarity index 100% rename from guides/images/ory-ecosystem.png rename to _legacy/_master/guides/images/ory-ecosystem.png diff --git a/guides/images/ory-ecosystem.xml b/_legacy/_master/guides/images/ory-ecosystem.xml similarity index 100% rename from guides/images/ory-ecosystem.xml rename to _legacy/_master/guides/images/ory-ecosystem.xml diff --git a/_legacy/_master/guides/keto/1-overview/1-policies.md b/_legacy/_master/guides/keto/1-overview/1-policies.md new file mode 100644 index 000000000..cadbbecdf --- /dev/null +++ b/_legacy/_master/guides/keto/1-overview/1-policies.md @@ -0,0 +1,184 @@ +# Access Control Policies + + + +## Overview + +If you have ever worked with cloud providers, you have probably encountered IAM (Identity & Access Management) Policies. +Amazon Web Services' IAM services is the champion of enterprise IT which can be attributed to +[AWS IAM Policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html). + +ORY Keto uses our [Go SDK ORY Ladon](https://github.com/ory/ladon) to provide a self-hosted service that implements +a comparable experience to AWS IAM Policies, or more generally, Access Control Policies. + +## Users & Permissions + +Before we take a look at Access Control Policies in detail, let's get some of the basics figured out. Every app that +has users usually assigns permissions to these users ("Bob, Alice are allowed to write blog posts"). There are +various established practices for assigning one or more permissions to one or more users. + +In the context of access control, you'll often encounter **identities** or **subjects** as an alias for users. In this +documentation we use the terminology **subject** as it summarizes users, robots, cronjobs, services, ... best. So +whenever you read "subject" you can substitute it with users, if it helps your understanding. + +Let's begin with a short overview of the established concepts surrounding permissions. Please be aware that the next +sections are merely an overview of those topics and aimed at giving you some context. They do not explain all the +nuances of each respective approach. + +### Access Control Lists (ACL) + +An [Access Control List (ACL)](https://en.wikipedia.org/wiki/Access_control_list) is a matrix of users and permissions: + +| | blog_post.create | blog_post.delete | blog_post.modify | blog_post.read | +|-------|------------------|------------------|------------------|----------------| +| alice | yes | yes | yes | yes | +| bob | no | no | no | yes | +| peter | yes | no | yes | yes | + +In the example above, `alice` has the permission to create a blog post `(blog_post.create)` while bob des not. All three +(alice, bob, peter) can read blog posts. + +Similarly, you can create a matrix of resources (e.g. blog articles) and each user's permissions +(`c` for `create`, `m` for `modify`, ...) with regards to that resource: + +| | blog_post.1 | blog_post.2 | blog_post.3 | blog_post.4 | +|------- |------------- |------------- |------------- |------------- | +| alice | c,r,m,d | c,r,m,d | c,r,m,d | c,r,m,d | +| bob | r | r | r | r | +| peter | c,r,m,d | r | c,r,m,d | r | + +ACLs are common in filesystems (`chmod` / `chown`) and in applications with few subjects. However, the matrix becomes +unreadable and unmanagable if you have thousands or even millions of subjects. Therefore, ACLs are rarely used in +web applications. + +### Role Based Access Control (RBAC) + +[Role Based Access Control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) maps subjects to roles +and roles to permissions. The goal of RBAC is to make permission management convenient by grouping subjects +in roles and assigning permissions roles. This type of access control is very common in web applications, where you often +encounter roles such as "administrator", "moderator", and so on. + +What's common in RBAC is that roles can inherit permissions from one another. The role administrator, for example, +could inherit all permissions from role moderator. This further decreases management complexity as, instead of adding +all permissions to administrator or assigning a user to both moderator and administrator roles, you simply point the +administrator role to inherit from the moderator one. + +Let's come back to alice, bob, peter, and blog posts and the matrix from the ACL example, but this time we define +roles "reader", "author", "admin" and model the ACL example using RBAC: + +![RBAC Example](../images/rbac.png). + +As you can see, `admin` inherits from `author`, which inherits from `reader`. Only `alice` (or rather `admin`) can delete blog posts, +whereas `author` can create and modify blog posts. We assign the roles to our subjects `bob`, `peter`, `alice` and +express the same permissions as in the ACL example. + +RBAC is everywhere. If you ever installed a forum software such as [phpBB](https://www.phpbb.com/support/docs/en/3.1/ug/adminguide/permissions_roles/), +[Wordpress](https://codex.wordpress.org/Roles_and_Capabilities) or others, you have definitely encountered ACL, RBAC, or both. + +RBAC reduces management complexity & overhead with large user/subject bases. Sometimes however, RBAC is not enough as well. +That's the case when you're trying to express ownership (e.g. `bob` can modify blog posts, but only his own), or +have attributes (e.g. `bob` works in department `blog`), or multi-tenant environments. + +### Access Control Policies (ACP) + +Access Control Policies (usually JSON "documents") define an `effect`, `subject`, `action`, and `resource`. For example, `alice` (subject) is +`allowed` (effect) to `delete` (action) blog article with ID `my-first-blog-post` (`resource`). This is very similar +to how ACLs work: + +```json +{ + "subjects": ["alice"], + "resources": ["blog_posts:my-first-blog-post"], + "actions": ["delete"] + "effect": "allow" +} +``` + +The policy above allows `alice` to `delete` `blog_posts:my-first-blog-post`. We could apply this policy to more subjects +and also more actions or resources, if we want to: + +```json +{ + "subjects": ["alice", "bob"], + "resources": ["blog_posts:my-first-blog-post", "blog_posts:2", "blog_posts:3"], + "actions": ["delete", "create", "read", "modify"] + "effect": "allow" +} +``` + +Well, this looks like ACL in disguise so far. So what's different? + +#### Precedence + +The first difference is that we can explicitly deny access: + +```json +{ + "subjects": ["peter"], + "resources": ["blog_posts:my-first-blog-post", "blog_posts:2", "blog_posts:3"], + "actions": ["delete", "create", "read", "modify"] + "effect": "deny" +} +``` + +The policy decision point (the one checking if something is allowed or not) applies the following rule set when deciding if something is allowed or not: + +1. If at least one policy for a given subject, action, and resource matches, and the effect is `deny`, the request is always denied. +2. If no policy matches with effect `deny`, but at least one policy with effect `allow`, the request is allowed. +3. If no policy matches at all, the request is denied. + +#### Pattern Matching with Regular Expressions + +ORY Keto allows you to apply pattern matching with regular expressions as well. Depending on how you name your subjects, +resources, and actions (for more on that topic go to the [Best Practices](./4-best-practices) section), you can apply +pattern matching using regular expressions. + +```json +{ + "subjects": ["users:<.*>"] +} +``` + +In the example above, the (incomplete) policy would match every subject that is prefixed with `users:`, so for example +`users:alice`, `users:bob`. In ORY Ladon / ORY Keto, regular expressions are delimited with `<` and `>`. +For example, `"users:.*"` is not a valid regular expression, just a simple string. + +The next example will allow all subjects with prefix `user:` to read (`actions:read`) all resources that match `resources:blog_posts:<[0-9]+>` +(e.g. `resources:blog_posts:1234` but not `resources:blog_posts:abcde`): + +```json +{ + "subjects": ["users:<.*>"], + "resources": ["resources:blog_posts:<[0-9]+>"], + "actions": ["actions:read"] + "effect": "allow" +} +``` + +#### Conditions + +Conditions are another powerful concept. So far, we covered that an Access Control Policies applies to a list of +`subjects`, `resources`, and `actions`. Conditions narrow down the use cases in which a certain ACP applies. A condition +may, for example, mandate that the IP Address of the client making the request has to match e.g. `192.168.0.0/16` or that +the subject is also the owner of the resource: + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "remoteIPAddress": { + "type": "CIDRCondition", + "options": { + "cidr": "192.168.0.0/16" + } + } + } +} +``` + +What conditions you can use and how you use is documented [here](./5-conditions). Since that requires knowledge of the Warden API +works, let's look at that first in the [next chapter](./2-warden). diff --git a/_legacy/_master/guides/keto/1-overview/2-warden.md b/_legacy/_master/guides/keto/1-overview/2-warden.md new file mode 100644 index 000000000..8fc2e4e45 --- /dev/null +++ b/_legacy/_master/guides/keto/1-overview/2-warden.md @@ -0,0 +1,188 @@ +# Access Control: The Warden API + + + +## Overview + +So far you learned how Access Control Policies work and how they are different from RBAC and ACL. Now, you will learn +how to use the Warden API to perform access control (decide if a request should be allowed or not). + +Before making a decision, we have to authenticate the subject. ORY Keto supports different authentication mechanisms +explained in the later sections. But first we'll look at how access requests work if we use "plaintext" authentication. + +Let's take a very simple policy: + +```json +{ + "subjects": ["alice"], + "resources": ["blog_posts:my-first-blog-post"], + "actions": ["delete"] + "effect": "allow" +} +``` + +By the way, you can create this policy with the ORY Keto CLI: + +``` +# We assume that your keto server runs at http://your-keto-server +$ keto policies create --endpoint http://your-keto-server \ + -a delete \ + -s alice \ + -r "blog_posts:my-first-blog-post" + --allow +``` + +Ok, so let's say a request hits our blog post API. The subject (user) that made the request is `bob` and `bob` wants +to delete the blog post `blog_posts:my-first-blog-post`. You will make a REST request to ORY Keto's Warden API: + +``` +$ curl -X POST http://your-keto-server/warden/subjects/authorize \ + --header "Content-Type: application/json" \ + -d @- << EOF + +{ + "subject": "bob", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +EOF +``` + +In this case, the response would be `{ "allowed": false }` because no policy matching `bob`, `delete`, +`blog_posts:my-first-blog-post` was found. If we were to make the same Warden API requests with alice + +``` +# curl -X POST http://your-keto-server/warden/subjects/authorize \ + --header "Content-Type: application/json" \ + -d @- << EOF + +{ + "subject": "alice", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +EOF +``` + +the response would be true because a policy was found and the effect was `allow`: `{ "allowed": true }`. You can +use the ORY Keto CLI to make the same request: + +``` +$ keto warden authorize subject --endpoint http://your-keto-server \ + --action delete \ + --resource "blog_posts:my-first-blog-post" \ + --subject alice +``` + +## Authentication + +ORY Keto supports different authentication methods at the Warden API. So far, we used the `subject` authenticator +which does not authenticate at all but simply takes the value of `subject` key in the request body. Currently supported +authenticators are: + +* `/warden/subjects/authorize`: The subject ("plaintext") authenticator we already used. +* `/warden/oauth2/access-tokens/authorize`: Validates OAuth 2.0 Access Token using the OAuth 2.0 Token Introspection +standard to resolve Access Tokens to subjects. +* `/warden/oauth2/clients/authorize`: Validates OAuth 2.0 Client Credentials using the OAuth 2.0 Client Credentials +Grant to authenticate OAuth 2.0 Clients. + +We intend on adding more authenticators such as JSON Web Tokens, SAML Assertions, and others in time. + +Let's take a deeper look at all of the available authenticators + +### Subject Authenticator + +You know this one already. The endpoint is located at `/warden/subjects/authorize` and you use request bodies such +as: + +``` +{ + "subject": "alice", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +The `subject` key is used as `subject` when looking for policies. No magic here, it's like authenticating with just +a username. + +### OAuth 2.0 Access Token Authenticator + +This authenticator is located at `/warden/oauth2/access-tokens/authorize` and is able to validate OAuth 2.0 Access Tokens +using the OAuth 2.0 Token Introspection standard. Instead of supplying a `subject`, you add an OAuth 2.0 Token and +optionally the OAuth 2.0 Scope to the request payload: + +``` +{ + "token": "some.access.token.57jgoi19g", + "scope": ["scope-a", "scope-b"], + + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +Let's assume for a second, that the token `some.access.token.57jgoi19g` was granted by subject `alice` with OAuth 2.0 Scope +`scope-a` and `scope-b`. In this case, the OAuth 2.0 Token Introspection endpoint would return a positive response (the token +is valid) and also the subject `alice`. So this authenticator looks for a policy that matches: + +``` +{ + "subject": "alice", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +#### Configuration + +You can configure this authenticator using the following environment variables: + +* `AUTHENTICATOR_OAUTH2_INTROSPECTION_URL`: The URL of the OAuth 2.0 Introspection Endpoint (usually `/oauth2/introspection`). +* `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE_STRATEGY`: Since you can include a `scope` in the request body, ORY Keto +allows you to configure a OAuth 2.0 strategy that defines how to compare OAuth 2.0 Scope: + * `hierarchic`: Scope `foo` matches `foo`, `foo.bar`, `foo.baz` but not `bar` + * `wildcard`: Scope `foo.*` matches `foo`, `foo.bar`, `foo.baz` but not `bar`. Scope `foo` matches `foo` but not `foo.bar` nor `bar` + * `exact`: Scope `foo` matches `foo` but not `bar` nor `foo.bar` +* In cases where the OAuth 2.0 Introspection endpoint is protected using OAuth 2.0 Access Tokens, you can configure ORY Keto to +authorize before calling this endpoint with the following configuration: + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_CLIENT_ID`: The OAuth 2.0 Client ID that should be used to make the OAuth 2.0 + Introspection request. + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_CLIENT_SECRET`: The OAuth 2.0 Client Secret that should be used to make the OAuth 2.0 + Introspection request. + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_TOKEN_URL`: The URL of the OAuth 2.0 Token Endpoint (usually `/oauth2/token`). + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE`: If an OAuth 2.0 Scope is required to access the introspection URL, add that here. + You can define a list using the comma delimiter: `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE=scope-a,scope-b`. + +### OAuth 2.0 Client Credentials Authenticator + +This authenticator is located at `/warden/oauth2/clients/authorize` and is able to authenticate OAuth 2.0 Clients +using the OAuth 2.0 Client Credentials grant. A request payload looks as follows: + +``` +{ + "client_id": "client-id", + "client_secret": "client-secret", + "scope": ["scope-a", "scope-b"], + + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +If the OAuth 2.0 Token endpoint returns an access token, the OAuth 2.0 Clients is considered authenticated. The OAuth 2.0 +Client ID is used as subject when querying for ACPs: + +``` +{ + "subject": "client-id", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +#### Configuration + +This endpoint needs only one environment variable: + +* `AUTHENTICATOR_OAUTH2_CLIENT_CREDENTIALS_TOKEN_URL`: The URL of the OAuth 2.0 Token Endpoint (usually `/oauth2/token`). \ No newline at end of file diff --git a/_legacy/_master/guides/keto/1-overview/3-roles.md b/_legacy/_master/guides/keto/1-overview/3-roles.md new file mode 100644 index 000000000..d2cd6aae0 --- /dev/null +++ b/_legacy/_master/guides/keto/1-overview/3-roles.md @@ -0,0 +1,71 @@ +# Roles + +ORY Keto supports the concept of roles (like in RBAC). This feature allows you to group together a number of subjects +under the same role. Whenever making a request to the Warden API, it will check the role's of a subject (if there are any) +and use them when looking up Access Control Policies. + +Assuming the following policies: + +```json +{ + "subjects": ["bob"], + "resources": ["blog_posts:my-first-blog-post"], + "actions": ["create"] + "effect": "allow" +} +``` + +```json +{ + "subjects": ["admin"], + "resources": ["blog_posts:my-first-blog-post"], + "actions": ["delete"] + "effect": "allow" +} +``` + +As you can see, `bob` is allowed to create resource `blog_posts:my-first-blog-post` and `admin` is allowed to delete it. +Warden request + +``` +{ + "subject": "bob", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +will return `{ "allowed": false }` while Warden request + +``` +{ + "subject": "admin", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +will return `{ "allowed": true }`. If we add `bob` to role `admin` using the [Role API](https://www.ory.sh/docs/api/keto) +or the CLI: + +``` +$ keto roles create admin \ + --endpoint http://your-keto-server/ + +$ keto roles members add admin bob \ + --endpoint http://your-keto-server/ +``` + +and redo the same Warden request + +``` +{ + "subject": "bob", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +the response will return `{ "allowed": true }` as well. + +Currently, we do not support hierarchy in roles but we might add that at a later stage. diff --git a/_legacy/_master/guides/keto/1-overview/4-best-practices.md b/_legacy/_master/guides/keto/1-overview/4-best-practices.md new file mode 100644 index 000000000..127140081 --- /dev/null +++ b/_legacy/_master/guides/keto/1-overview/4-best-practices.md @@ -0,0 +1,50 @@ +# Best Practices + +This sections gives an overview of best practices for access control policies +we developed over the years at ORY. + + + +## Scalability + +Access Control Policies not using any regular expressions are quite scalable. Defining many regular expressions and +a lot of policies (50.000+) may have a notable performance impact on CPU, your database and generally increase response +times. This is because regular expressions can not be indexed. Regular expressions have a complexity of `O(n)` in Go, +but that is still getting slow when you define too many. + +Try to solve your access control definitions with a few generalized policies, and try to leverage Warden Groups. + +## URNs + +> “There are only two hard things in Computer Science: cache invalidation and naming things.” +-- Phil Karlton + +URN naming is as hard as naming API endpoints. Thankfully, by doing the latter, the former is usually solved as well. +We will explore further best practices in the following sections. + +## Scope the Organization Name + +A rule of thumb is to prefix resource names with a domain that represents the organization creating the software. + +* **Do not:** `` +* **Do:** `:` + +## Scope Actions, Resources and Subjects + +It is wise to scope actions, resources, and subjects in order to prevent name collisions: + +* **Do not:** `myorg.com:`, `myorg.com:`, `myorg.com:` +* **Do:** `myorg.com:subjects:`, `myorg.com:resources:`, `myorg.com:actions:` +* **Do:** `subjects:myorg.com:`, `resources:myorg.com:`, `actions:myorg.com:` + +## Multi-Tenant Systems + +Multi-tenant systems typically have resources which should not be access by other tenants in the system. This can be +achieved by adding the tenant id to the URN: + +* **Do:** `resources:myorg.com:tenants::` + +In some environments, it is common to have organizations and projects belonging to those organizations. Here, the +following URN semantics can be used: + +* **Do:** `resources:myorg.com:organizations::projects::` diff --git a/_legacy/_master/guides/keto/1-overview/5-conditions.md b/_legacy/_master/guides/keto/1-overview/5-conditions.md new file mode 100644 index 000000000..ec38630d5 --- /dev/null +++ b/_legacy/_master/guides/keto/1-overview/5-conditions.md @@ -0,0 +1,301 @@ +# Conditions and Context + + + +## Overview + +Conditions are defined in policies. Contexts are defined in access control requests. Conditions use contexts and decide +if a policy is responsible for handling the access request at hand. + +Conditions are functions returning true or false given a context. Because conditions implement logic, +they must be programmed. ORY Keto uses conditions defined in [ORY Ladon](https://github.com/ory/ladon/#conditions). +Adding new condition handlers must be done through creating a pull request in the ORY Ladon repository. + +A condition has always the same JSON format: + +```json +{ + "subjects": ["..."], + "actions" : ["..."], + "effect": "allow", + "resources": ["..."], + "conditions": { + "this-key-will-be-matched-with-the-context": { + "type": "SomeConditionType", + "options": { + "some": "configuration options set by the condition type" + } + } + } +} +``` + +The context in the access request made to ORY Keto's Warden API must match the specified key in the condition +in order to be evaluated by the condition logic: + +```json +{ + "subject": "...", + "action" : "...", + "resource": "...", + "context": { + "this-key-will-be-matched-with-the-context": { "foo": "bar" } + } +} +``` + +### CIDR Condition + +The CIDR condition matches CIDR IP Ranges. An exemplary policy definition could look as follows. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "remoteIPAddress": { + "type": "CIDRCondition", + "options": { + "cidr": "192.168.0.0/16" + } + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "remoteIPAddress": "192.168.0.5" + } +} +``` + +The next access request would be denied as the condition is not fulfilled and thus no policy is matched. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "remoteIPAddress": "255.255.0.0" + } +} +``` + +The next access request would also be denied as the context is not using the key `remoteIPAddress` but instead `someOtherKey`. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someOtherKey": "192.168.0.5" + } +} +``` + +### String Equal Condition + +Checks if the value passed in the access request's context is identical with the string that was given initially. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "someKeyName": { + "type": "StringEqualCondition", + "options": { + "equals": "the-value-should-be-this" + } + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "the-value-should-be-this" + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "this-is-a-different-value" + } +} +``` + +### String Match Condition + +Checks if the value passed in the access request's context matches the regular expression that was given initially. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "someKeyName": { + "type": "StringMatchCondition", + "options": { + "equals": "regex-pattern-here.+" + } + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "regex-pattern-here-matches" + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "regex-pattern-here" + } +} +``` + +### Subject Condition + +Checks if the access request's subject is identical with the string specified in the context. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "owner": { + "type": "EqualsSubjectCondition", + "options": {} + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "owner": "users:maria" + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "owner": "another-user" + } +} +``` + +This condition makes more sense when being used with access tokens where the subject is extracted from the token. + +### String Pairs Equal Condition + +Checks if the value passed in the access request's context contains two-element arrays and that both elements in each pair are equal. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "someKey": { + "type": "StringPairsEqualCondition", + "options": {} + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKey": [ + ["some-arbitrary-pair-value", "some-arbitrary-pair-value"], + ["some-other-arbitrary-pair-value", "some-other-arbitrary-pair-value"] + ] + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKey": [ + ["some-arbitrary-pair-value", "some-other-arbitrary-pair-value"] + ] + } +} +``` diff --git a/_legacy/_master/guides/keto/1-overview/6-installation.md b/_legacy/_master/guides/keto/1-overview/6-installation.md new file mode 100644 index 000000000..b3dd3994e --- /dev/null +++ b/_legacy/_master/guides/keto/1-overview/6-installation.md @@ -0,0 +1,32 @@ +# Installing ORY Keto + + + +You can install ORY Keto by downloading the [binaries](https://github.com/ory/keto/releases), by using +the precompiled Docker Image available at [Docker Hub](https://hub.docker.com/r/oryd/keto/), or by +compiling the code yourself. + +## Docker Hub + +The recommended way to install and run ORY Keto is via docker: + +```sh +$ docker run oryd/keto: help +``` + +## Binaries + +If you download the binaries, make sure to add them to your path (e.g. `/usr/bin`). Then, run `keto help` + +## From Source + +To install ORY Keto from source, you need to have Go 1.10+ installed as well as [go/dep](https://golang.github.io/dep/). +Then, run: + +``` +$ go get -d -u github.com/ory/keto +$ cd $GOPATH/src/github.com/ory/keto +$ dep ensure -vendor-only +$ go install . +$ keto help +``` diff --git a/_legacy/_master/guides/keto/1-overview/7-configuration.md b/_legacy/_master/guides/keto/1-overview/7-configuration.md new file mode 100644 index 000000000..125fc5377 --- /dev/null +++ b/_legacy/_master/guides/keto/1-overview/7-configuration.md @@ -0,0 +1,134 @@ +# Configuring and Running ORY Keto + +ORY Keto supports two types of storage adapters: + +* In-memory: This adapter does not work with more than one instance ("cluster") and any state is lost after restarting the instance. +* SQL: This adapter works with more than one instance and state is not lost after restarts. + +The SQL adapter supports two DBMS: PostgreSQL 9.6+ and MySQL 5.7+. Please note that +older MySQL versions have issues with the database schema. +For more information [go here](https://github.com/ory/hydra/issues/377). + +ORY Keto supports various authentication strategies. Depending on what strategies +you want to use, you will have to configure more services (e.g. ORY Hydra). In this tutorial, we will +set up ORY Keto without any of the other services. Please refer to the [warden chapter](./2-warden) +to see how to configure each authentication strategy. + +This guide will: + +1. Download and run a PostgreSQL container in Docker. +2. Download and run ORY Keto using Docker. + +## Create a Network + +Before we can start, a network must be created which we will attach all our Docker containers to. That way, the containers +can talk to one another. + +``` +$ docker network create ketoguide +``` + +## Start the PostgreSQL Container + +For the purpose of this tutorial, we will use PostgreSQL as a database. As you probably already know, don't run databases in Docker in production! +For the sake of this tutorial however, let's use Docker to quickly deploy the database. + +``` +$ docker run \ + --network ketoguide \ + --name ory-keto-example--postgres \ + -e POSTGRES_USER=keto \ + -e POSTGRES_PASSWORD=secret \ + -e POSTGRES_DB=keto \ + -d postgres:9.6 +``` + +This command wil start a postgres instance with name `ory-keto-example--postgres`, set up a database called `keto` +and create a user `keto` with password `secret`. + +## Run the ORY Keto Service + +``` +# The database url points us at the postgres instance. This could also be an ephermal in-memory database (`export DATABASE_URL=memory`) +# or a MySQL URI. +$ export DATABASE_URL=postgres://keto:secret@ory-keto-example--postgres:5432/keto?sslmode=disable + +# This pulls the latest image from Docker Hub +$ docker pull oryd/keto:v1.0.0-beta.5 + +# ORY Keto does not do magic, it requires conscious decisions, for example running SQL migrations which is required +# when installing a new version of ORY Keto, or upgrading an existing installation. +# It is the equivalent to `keto migrate sql postgres://keto:secret@ory-keto-example--postgres:5432/keto?sslmode=disable` +$ docker run -it --rm \ + --network ketoguide \ + oryd/keto:v1.0.0-beta.5 \ + migrate sql $DATABASE_URL + +Applying `client` SQL migrations... +[...] +Migration successful! + +# Next, let's run the server! +$ docker run -d \ + --name ory-keto-example--keto \ + --network ketoguide \ + -p 4466:4466 \ + -e DATABASE_URL=$DATABASE_URL \ + oryd/keto:v1.0.0-beta.5 \ + serve +``` + +Great, the server running now! Make sure to check the logs and see if there were +any errors or issues before going to the next steps: + +``` +$ docker logs ory-keto-example--keto +``` + +## Running CLI Commands + +You can now create your first policy: + +``` +$ docker run -it --rm \ + --network ketoguide \ + oryd/keto:v1.0.0-beta.5 \ + policies create --endpoint http://ory-keto-example--keto:4466/ \ + --id example-policy \ + --allow \ + -a delete \ + -s alice \ + -r "blog_posts:my-first-blog-post" +``` + +List all existing policies: + +``` +$ docker run -it --rm \ + --network ketoguide \ + oryd/keto:v1.0.0-beta.5 \ + --endpoint http://ory-keto-example--keto:4466/ \ + policies get example-policy +``` + +And make some Warden requests: + +``` +$ docker run -it --rm \ + --network ketoguide \ + oryd/keto:v1.0.0-beta.5 \ + warden authorize subject --endpoint http://ory-keto-example--keto:4466/ \ + --action delete \ + --subject alice \ + --resource "blog_posts:my-first-blog-post" +``` + +## Securing ORY Keto + +Similar to other services in our ecosystem, ORY Keto has no native access control. This means that any request +made to e.g. `/policies` or `/warden/...` is considered authenticated and thus executed. However, these endpoints +are very sensitive as they define who is allowed to do what in your system. + +Please use an API Gateway or a similar mechanism to protect these endpoints. How you protect them, is up to you. + +If you require dedicated help with this, consider asking us for [consultancy](mailto:hi@ory.sh). diff --git a/guides/keto/1-overview/README.md b/_legacy/_master/guides/keto/1-overview/README.md similarity index 100% rename from guides/keto/1-overview/README.md rename to _legacy/_master/guides/keto/1-overview/README.md diff --git a/_legacy/_master/guides/keto/README.md b/_legacy/_master/guides/keto/README.md new file mode 100644 index 000000000..189477eb1 --- /dev/null +++ b/_legacy/_master/guides/keto/README.md @@ -0,0 +1,17 @@ +# Introduction + +Welcome to the ORY Keto documentation! + +ORY Keto is a service that stores permissions and can answer if an identity is allowed to perform a certain action over +REST. If you came here to answer + +* if a certain user is allowed to modify e.g. a blog article +* if a robot is allowed to e.g. print a document + +you have come to the right place. If you came here, because your permission system involves + +* complex multi-tenant environments +* permissions based on ownership, ip address, time of day +* or other advanced uses + +you are the right place even more so. \ No newline at end of file diff --git a/guides/keto/images/rbac.png b/_legacy/_master/guides/keto/images/rbac.png similarity index 100% rename from guides/keto/images/rbac.png rename to _legacy/_master/guides/keto/images/rbac.png diff --git a/guides/keto/images/rbac.xml b/_legacy/_master/guides/keto/images/rbac.xml similarity index 100% rename from guides/keto/images/rbac.xml rename to _legacy/_master/guides/keto/images/rbac.xml diff --git a/_legacy/_master/guides/oathkeeper/1-overview/1-rules.md b/_legacy/_master/guides/oathkeeper/1-overview/1-rules.md new file mode 100644 index 000000000..48f194bc4 --- /dev/null +++ b/_legacy/_master/guides/oathkeeper/1-overview/1-rules.md @@ -0,0 +1,797 @@ +# Access Rules + +ORY Oathkeeper decides whether or not access should be allowed with Access Rules. Access Rules can be managed using the +ORY Oathkeeper API. + + + +## Overview + +An ORY Oathkeeper Access Rule has the following layout: + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": {/* ... */}, + "credentials_issuer": { /* ... */ } +} +``` + +In this case, if a request to `http://my-app/some-route` is made (this is where ORY Oathkeeper will listen to), then +the rule with ID `some-id` will be executed. Then: + +* If the request hits the ORY Oathkeeper proxy (`oathkeeper serve proxy`): The request will be forwarded to the upstream URL. +* If the request hits the ORY Oathkeeper judge (`oathkeeper serve api` -> `/judge/some-route`): The server will respond with +status code 200 if the request is valid and any other status code if not. + +The `match.url` value is capable of parsing regular expressions. Value `http://my-app/some-route` will only +match this exact URL, not `http://my-app/some-route/foo`, `http://my-app/some-ROUTE`, nor `https://my-app/some-route`. + +Regular expressions are delimited with `<` and `>`. A `match.url` value of `http://my-app/some-route<.*>` will match +`http://my-app/some-route/foo`, `http://my-app/some-route`, `http://my-app/some-routeABCDEF`, and so on. You can +use multiple regular expressions: `://my-app/some-route<.*>`. + +The `match.methods` value defines which HTTP methods this access rule will match. The rule `some-id` will, for example, +not match POST requests. + +ORY Oathkeeper will throw an error if more than one access rule is found for a given HTTP request. You have to make sure +that your access rules don't overlap. + +Once an access rule is matched, ORY Oathkeeper will authenticate the credentials, authorize the request subject (e.g. the user), +and transform the credentials. More on that in the next sections. + +### Authenticators + +An authenticator is responsible for authenticating request credentials. ORY Oathkeeper supports different authenticators +and we will add more as the project progresses. + +An authenticator inspects the HTTP request (e.g. the HTTP Authorization Header) and executes some business logic that +returns true (for authentication ok) or false (for authentication invalid) as well as a subject ID. The subject ID is +typically the "user" that made the request, but it could also be a machine (if you have machine-2-machine interaction) or +something different. + +Each authenticator has the same configuration layout + +``` +"authenticators": [ + { + "handler": "a", + "config": {/* depends on the authenticator */} + } +] +``` + +where `handler` is the name of the authenticator and `config` is an optional configuration for that specific +authenticator. + +You can define more than one authenticator in the access rule. The first authenticator that is able to handle the +credentials will be consulted and other authenticators will be ignored: + +``` +"authenticators": [ + { + "handler": "a" + }, + { + "handler": "b" + }, + { + "handler": "c" + } +] +``` + +If handler `a` is able to handle the provided credentials, then handler `b` and `c` will be ignored. If handler `a` +can not handle the provided credentials but handler `b` can, then handler `a` and `c` will be ignored. Handling +the provided credentials means that the authenticator knows how to handle, for example, the `Authorization: basic` header. +It does not mean that the credentials are valid! If a handler encounters invalid credentials, then other handlers will be ignored too. + + +Let's take a look at the different supported authenticators! + +#### `noop` + +This is a special authenticator. It tells ORY Oathkeeper to bypass the authentication process. This also implies +that no authorization will be executed and no credentials will be issued. It's basically a pass-all authenticator +that allows any request to be forwarded to the upstream URL. + +##### Example + +The following rule allows all requests to `GET http://my-app/some-route`: + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [{ + "handler": "noop" + }] +} +``` + +#### `anonymous` + +The `anonymous` authenticator checks whether or not an `Authorization` header is set. If not, it will use the value +of the environment variable `AUTHENTICATOR_ANONYMOUS_USERNAME` as the subject name. + +##### Example + +The following rule allows all requests to `GET http://my-app/some-route` and sets the subject name to the value +of the environment variable `AUTHENTICATOR_ANONYMOUS_USERNAME`, as long as no `Authorization` header is set in the +HTTP request: + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [{ + "handler": "anonymous" + }], + /* ... */ +} +``` + +#### `oauth2_client_credentials` + +This `oauth2_client_credentials` uses the username and password from HTTP Basic Authorization (`Authorization: basic base64()` +to perform the OAuth 2.0 Client Credentials grant in order to detect if the provided credentials are valid. + +To use this authenticator, you must provide the environment variable `AUTHENTICATOR_OAUTH2_CLIENT_CREDENTIALS_TOKEN_URL` +which sets the OAuth 2.0 Token URL that should be used to check if the provided credentials are valid or not. For +example, you could set it as follows: + +``` +$ export AUTHENTICATOR_OAUTH2_CLIENT_CREDENTIALS_TOKEN_URL=https://my-oauth2-server/oauth2/token +``` + +This authenticator will use the username from the HTTP Basic Authorization header as the subject for this request. + +This authenticator has one configuration option which is `required_scope`. This option sets what scope is +required by the URL and when making performing OAuth 2.0 Client Credentials request, the scope will be included +in the request. + +##### Example + +The following rule allows requests to `GET http://my-app/some-route` if valid a OAuth 2.0 Client ID and Client Secret +has been provided and if that client is allowed to request scope `scope-a` and `scope-b`: + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [{ + "handler": "oauth2_client_credentials", + "config": { + "required_scope": ["scope-a", "scope-b"] + } + }], + /* ... */ +} +``` + +#### `oauth2_introspection` + +The `oauth2_introspection` authenticator handles requests that have an Bearer Token in the Authorization Header (`Authorization: bearer `). +It then uses OAuth 2.0 Token Introspection to check if the token is valid and if the token was granted the requested scope. + +This authenticator is a bit more complex to set up. You have to define the following environment variables: + +* Required + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_URL`: The OAuth 2.0 Token Introspection URL. +* In cases where the OAuth 2.0 Introspection Endpoint is protected and requires an OAuth 2.0 Access Token, you can configure +ORY Oathkeeper to authorize before making requests to that endpoint with the following, optional, settings: + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_CLIENT_ID`: The OAuth 2.0 Client ID the client that performs the OAuth 2.0 Token Introspection. The OAuth 2.0 Token Introspection + endpoint is typically protected and requires a valid OAuth 2.0 Client in order to check if a token is valid or not. + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_CLIENT_SECRET`: The OAuth 2.0 Client Secret of the client that performs the OAuth 2.0 Token Introspection. + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_TOKEN_URL`: The OAuth 2.0 Token URL. + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE`: If the OAuth 2.0 Token Introspection endpoint requires a certain OAuth 2.0 Scope + in order to be accessed, you can set it using this environment variable. Use commas to define more than one OAuth 2.0 Scope. + Example: `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE=scope-a,scope-b` + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE_STRATEGY`: The strategy to be used to validate the scope claim. Strategies `HIERARCHIC`, `EXACT`, + `WILDCARD`, `NONE` are supported. Defaults to `EXACT`. For more information on scope strategies, click [here](#scope-strategies)- + + +##### Example + +The following rule allows requests to `GET http://my-app/some-route` if valid a OAuth 2.0 Access Token was provided +and if that token was granted OAuth 2.0 Scope `scope-a` and `scope-b`: + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [{ + "handler": "oauth2_introspection", + "config": { + "required_scope": ["scope-a", "scope-b"] + } + }], + /* ... */ +} +``` + +#### `jwt` + +The `jwt` authenticator handles requests that have an Bearer Token in the Authorization Header (`Authorization: bearer `). +It assumes that the token is a JSON Web Token and tries to verify the signature of it. + +* Required + * `AUTHENTICATOR_JWT_JWKS_URL`: The URL where ORY Oathkeeper can retrieve JSON Web Keys from for validating + the JSON Web Token. Usually something like `https://my-keys.com/.well-known/jwks.json`. The response + of that endpoint must return a [JSON Web Key Set (JWKS)](https://auth0.com/docs/jwks). +* Optional + * `AUTHENTICATOR_JWT_SCOPE_STRATEGY`: The strategy to be used to validate the scope claim. Strategies `HIERARCHIC`, `EXACT`, + `WILDCARD`, `NONE` are supported. Defaults to `EXACT`. For more information on scope strategies, click [here](#scope-strategies)- + +##### Example + +The following rule allows requests to `GET http://my-app/some-route` if valid a JSON Web Token was provided +and if that token has scope `scope-a` and `scope-b`, audience `aud-1` and was issued by `iss-1`. All configuration +items are optional and ignored if left out. + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [{ + "handler": "jwt", + "config": { + "required_scope": ["scope-a", "scope-b"], + "target_audience": ["aud-1"], + "trusted_issuers": ["iss-1"] + } + }], + /* ... */ +} +``` + +Per default, this authenticator accepts JSON Web Tokens signed with the `RS256` algorithm only. You can change that +by whitelisting the algorithms to be allowed. All common JWT singing algorihtms (except `none`) are supported: + +``` +{ + /* ... */ + "authenticators": [{ + "handler": "jwt", + "config": { + "allowed_algorithms": ["RS256", "HS256", "RS512", "ES256"] + } + }], + /* ... */ +} +``` + +### Authorizers + +The authorizer takes the subject returned from the authenticator and checks if that subject is allowed to perform +the requested action. + +Each authorizer has the same configuration layout + +``` +"authorizer": { + "handler": "a", + "config": {/* depends on the authorizer */} +} +``` + +#### `allow` + +This authorizer allows every request to pass. + +#### Example + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": { + "handler": "allow" + } + /* ... */ +} +``` + +#### `deny` + +This authorizer denies every request. + +#### Example + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": { + "handler": "deny" + } + /* ... */ +} +``` + +#### `keto_warden` + +This authorizer uses the ORY Keto Warden API to perform sophisticated access control with access control policies. +Please familiarize yourself with the ORY Keto project before you set up this authorizer. + +To configure this authorizer, you must set the environment variable `AUTHORIZER_KETO_WARDEN_KETO_URL` to ORY Keto's URL, +for example `AUTHORIZER_KETO_WARDEN_KETO_URL=http://keto/`. **If this environment variable is not set, then this authorizer +will be disabled.** + +This authorizer has three configuration options, `required_action`, `required_resource` and `subject`: + +``` +"authorizer": { + "handler": "keto_warden", + "config": { + "required_action": "...", + "required_resource": "...", + "subject": "..." + } +} +``` + +These configuration options support variable expansion. Let's say you have the following match configuration: + +``` +"match": { + "url": "http://my-app/api/users/<[0-9]+>/<[a-zA-Z]+>", + "methods": ["GET"] +}, +``` + +Here, you have two regular expressions, `<[0-9]+>` and `<[a-zA-Z]+>`. You can reference the values matched by the +regular expression with `$1` and `$2` (and more generally `$n`) and they will be substituted before making the request +to ORY Keto: + +``` +"config": { + "required_action": "my:action:$1", + "required_resource": "my:resource:$2:foo:$1" +} +``` + +Assuming a request to `http://my-api/api/users/1234/foobar` was made, the config from above would expand to: + +``` +"config": { + "required_action": "my:action:1234", + "required_resource": "my:resource:foobar:foo:1234" +} +``` + +The `subject` field configures what subject is passed on to keto warden. +The `subject` value is a string which will be parsed by the Go [`text/template`](https://golang.org/pkg/text/template/) +package for value substitution, receiving the [`AuthenticationSession`](https://github.com/ory/oathkeeper/blob/92c09fb28552949cd034ed5555c87dfda91407a3/proxy/authenticator.go#L19) +struct: + +```go +type AuthenticationSession struct { + Subject string + Extra map[string]interface{} +} +``` + +If `subject` is not specified it will default to `AuthenticationSession.Subject`. + +Note that the `AuthenticationSession` struct has a field named `Extra` which is a `map[string]interface{}`, which receives +varying introspection data from the authentication process. Because the contents of `Extra` are so variable, nested and +potentially non-existent values need special handling by the `text/template` parser, and a `print` FuncMap function has +been provided to ensure that non-existent map values will simply return an empty string, rather than ``. + +If you find that your headers contain the string `` then you have most likely omitted the `print` function, and +it is recommended you use it for all values out of an abundance of caution and for consistency. + +#### Example + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/api/users/<[0-9]+>/<[a-zA-Z]+>", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": { + "handler": "keto_warden", + "config": { + "required_action": "my:action:$1", + "required_resource": "my:resource:$2:foo:$1" + "subject": "{{ .Extra.email }}" + } + } + /* ... */ +} +``` + + +### Credentials Issuers + +A credentials issuer translates the credentials from incoming requests to credentials that you backend understands. +For example, the `Authorization: basic` header might be transformed to `X-User: `. This allows you to +write backends that do not care if the original request was an anonymous one, an OAuth 2.0 Access Token, or some other +credential type. All your backend has to do is understand, for example, the `X-User:`. + +If you access ORY Oathkeeper using the judge endpoint, the header will be included in the HTTP response. + +#### `noop` + +This credentials issuer does not transform the HTTP request and simply forwards the headers as-is. This is useful +if you don't want to replace, for example, `Authorization: basic` with `X-User: `. + +##### Example + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/api/users/<[0-9]+>/<[a-zA-Z]+>", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": {/* ... */ } + "credentials_issuer" { + "handler": "noop" + } +} +``` + +#### `id_token` + +This credentials issuer takes the authentication information (e.g. subject ID) and transforms it to a signed JSON Web Token, +and more specifically to an OpenID Connect ID Token. You backend can verify the token by fetching the (public) key +from the `/.well-known/jwks.json` endpoint. + +Let's say a request is made to a resource protected by ORY Oathkeeper: + +``` +GET /api/resource HTTP/1.1 +Host: www.example.com +Authorization: bearer +``` + +Assuming that ORY Oathkeeper is granting the access request, the `` will be replaced with a +JSON Web Token that is signed using the asymmetric RS256 key: + +``` +GET /api/resource HTTP/1.1 +Host: internal-api-endpoint-dns +Authorization: bearer +``` + +Now, the protected resource is capable of decoding and validating the JSON Web Token using the public key supplied +by ORY Oathkeeper's API. The public key for decoding the ID token is available at Oathkeeper's `/.well-known/jwks.json` endpoint: + +``` +http://oathkeeper:4456/.well-known/jwks.json +``` + +That `.well-known/jwks.json` endpoint is not served by the ORY Oathkeeper proxy process, but by +the ORY Oathkeeper API process. You will learn more about the difference between the two in the next chapters. + +The related flow diagram looks like this: + +![ID Token Transformation](../images/id_token.svg) + +Let's say the `oauth2_client_credentials` authenticator successfully authenticated the credentials `client-id:client-secret`. +This credentials issuer will craft an ID Token (JWT) with the following exemplary claims: + +``` +{ + "iss": "https://server.example.com", + "sub": "client-id", + "aud": "s6BhdRkqt3", + "jti": "n-0S6_WzA2Mj", + "exp": 1311281970, + "iat": 1311280970, +} +``` + +The ID Token Claims are as follows: + +* `iss`: Issuer Identifier for the Issuer of the response. The iss value is a case sensitive URL using the https scheme + that contains scheme, host, and optionally, port number and path components and no query or fragment components. + Typically, this is the URL of ORY Oathkeeper, for example: `https://oathkeeper.myapi.com`. +* `sub`: Subject Identifier. A locally unique and never reassigned identifier within the Issuer for the End-User, which + is intended to be consumed by the Client, e.g., 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. It must not + exceed 255 ASCII characters in length. The sub value is a case sensitive string. The End-User might also + be an OAuth 2.0 Client, given that the access token was granted using the OAuth 2.0 Client Credentials flow. +* `aud`: Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party + as an audience value. It MAY also contain identifiers for other audiences. In the general case, the aud value is an + array of case sensitive strings. +* `exp`: Expiration time on or after which the ID Token MUST NOT be accepted for processing. The processing of this + parameter requires that the current date/time MUST be before the expiration date/time listed in the value. + Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the + date/time. See RFC 3339 [RFC3339] for details regarding date/times in general and UTC in particular. +* `iat`: Time at which the JWT was issued. Its value is a JSON number representing the number of seconds + from 1970-01-01T0:0:0Z as measured in UTC until the date/time. +* `jti`: A cryptographically strong random identifier to ensure the ID Token's uniqueness. + +This credentials issuer implements several token signing algorithms, specifically: + +- `HS256`: This algorithm uses a HMAC-SHA256 with a shared secret as opposed to private/public keys. This strategy +is not encouraged for production. +- `ORY-HYDRA`: This algorithm uses ORY Hydra's JWK management API to generate private/public RSA keypair. This strategy +is encouraged for use in production. + +You can set the strategy using the `CREDENTIALS_ISSUER_ID_TOKEN_ALGORITHM` environment variable. There also two more environment variables which modify +the behaviour of this strategy: + +* `CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN`: The lifespan of the ID Token which defaults to 10 minutes. Example: + `CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN=1s` (1 second), `CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN=1m` (1 minute), + `CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN=1h` (1 hour), `CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN=1d` (1 day) +* `CREDENTIALS_ISSUER_ID_TOKEN_ISSUER`: Who issued the token - this will be the value of the `iss` claim in the + ID Token. + +##### Token Signing Algorithms + +###### `HS256` + +The HS256 algorithm is the default one. This algorithm requires you to set the secret to be used for signing the ID Token. +Be aware that anyone in possession of this secret - also anyone having access to the `./well-known/jwks.json` URL +will be able to forge ID Tokens that will be accepted by your backends. We recommend using this strategy primarily +for development purposes. + +**Do not use this strategy in production unless you know what you are doing.** If you do use this in production, make sure +that **noone** (except the services validating the tokens) have access to the `./well-known/jwks.json` URL. + +Use must use the `CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET` environment variable to set the secret. + +###### `ORY-HYDRA` + +This ID Token signing algorithm uses ORY Hydra's JSON Web Key API to generate, store, and fetch a RSA public/private keypair. +When using this algorithm, you have the following environment variables available: + +* Required + * `CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_ADMIN_URL`: The URL where the ORY Hydra Admin API is located. +* Optional + * `CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_JWK_SET_ID`: The ID to be used to create & fetch the JSON Web Key from ORY Hydra. + Defaults to `oathkeeper:id-token`. + * `CREDENTIALS_ISSUER_ID_TOKEN_JWK_REFRESH_INTERVAL`: ORY Oathkeeper stores JSON Web Keys for ID Token signing in memory. + This value sets the refresh interval. Default is 5 minutes. +* If this endpoint is protected using OAuth 2.0 Access Tokens, you can configure ORY Oathkeeper to authorize before +calling this endpoint. To do so, configure the following, optional settings: + * CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_CLIENT_ID: The ID of the OAuth 2.0 Client. + * CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_CLIENT_SECRET: The secret of the OAuth 2.0 Client. + * CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_CLIENT_SCOPES: The OAuth 2.0 Scope the client should request. + * CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_PUBLIC_URL: The public URL where endpoint /oauth2/token is located. + +##### Access Rule Configuration + +Additionally, this credentials issuer allows you to specify the audience of the ID token per access rule. Setting +the audience is optional: + +``` +"credentials_issuer": { + "handler": "id_token", + "config": { + "aud": ["audience-1", "audience-2"] + } +} +``` + +##### Example + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/api/users/<[0-9]+>/<[a-zA-Z]+>", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": {/* ... */ } + "credentials_issuer": { + "handler": "id_token", + "config": { + "aud": ["audience-1", "audience-2"] + } + } +} +``` + +#### `headers` + +This credentials issuer will transform the request, allowing you to pass the credentials to the upstream application via +the headers. This will augment, for example, `Authorization: basic` with `X-User: `. + +The headers are specified via the `headers` field of the credentials issuer's `config` field. The keys are the header +name and the values are a string which will be parsed by the Go [`text/template`](https://golang.org/pkg/text/template/) +package for value substitution, receiving the [`AuthenticationSession`](https://github.com/ory/oathkeeper/blob/92c09fb28552949cd034ed5555c87dfda91407a3/proxy/authenticator.go#L19) +struct: + +```go +type AuthenticationSession struct { + Subject string + Extra map[string]interface{} +} +``` + +Note that the `AuthenticationSession` struct has a field named `Extra` which is a `map[string]interface{}`, which receives +varying introspection data from the authentication process. Because the contents of `Extra` are so variable, nested and +potentially non-existent values need special handling by the `text/template` parser, and a `print` FuncMap function has +been provided to ensure that non-existent map values will simply return an empty string, rather than ``. + +If you find that your headers contain the string `` then you have most likely omitted the `print` function, and +it is recommended you use it for all values out of an abundance of caution and for consistency. + +##### Example + +```json +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/api/<.*>", + "methods": ["GET"] + }, + "authenticators": [/* ... */], + "authorizer": {/* ... */} + "credentials_issuer" { + "handler": "headers", + "config": { + "headers": { + "X-User": "{{ print .Subject }}", + "X-Some-Arbitrary-Data": "{{ print .Extra.some.arbitrary.data }}" + } + } + } +} +``` + +#### `cookies` + +This credentials issuer will transform the request, allowing you to pass the credentials to the upstream application via +the cookies. + +The cookies are specified via the `cookies` field of the credentials issuers `config` field. The keys are the cookie name +and the values are a string which will be parsed by the Go [`text/template`](https://golang.org/pkg/text/template/) package +for value substitution, receiving the [AuthenticationSession](https://github.com/ory/oathkeeper/blob/92c09fb28552949cd034ed5555c87dfda91407a3/proxy/authenticator.go#L19) struct: + +```go +type AuthenticationSession struct { + Subject string + Extra map[string]interface{} +} +``` + +Note that the `AuthenticationSession` struct has a field name `Extra` which is a `map[string]interface{}`, which receives +varying introspection data from the authentication process. Because the contents of `Extra` are so variable, nested and +potentially non-existent values need special handling by the `text/template` parser, and a `print` FuncMap function has +been provided to ensure the non-existent map values will simply return an empty string, rather than ``. + +If you find that your cookies contain the string `` then you have most likely omitted the `print` function, and +it is recommended you use it for all values out of an abundance of caution and for consistency. + +##### Example + +```json +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/api/<.*>", + "methods": ["GET"] + }, + "authenticators": [/* ... */], + "authorizer": {/* ... */} + "credentials_issuer" { + "handler": "cookies", + "config": { + "cookies": { + "user": "{{ print .Subject }}", + "some-arbitrary-data": "{{ print .Extra.some.arbitrary.data }}" + } + } + } +} +``` + +## Scope Strategies + +The following scope strategies are supported: + +* `HIERARCHIC`: Scope `foo` matches `foo`, `foo.bar`, `foo.baz` but not `bar` +* `WILDCARD`: Scope `foo.*` matches `foo`, `foo.bar`, `foo.baz` but not `bar`. Scope `foo` matches `foo` but not `foo.bar` nor `bar` +* `EXACT`: Scope `foo` matches `foo` but not `bar` nor `foo.bar` +* `NONE`: Scope validation is disabled completely. It is expected that the upstream logic (e.g. OAuth 2.0 Token Introspection) handles scope validation +properly. If no upstream logic (e.g. JWT) exists, an error will be thrown if the scope is to be validated. + +## Rule Management + +### Rules REST API + +For more information on available fields and exemplary payloads of rules, as well as rule management using HTTP +please refer to the [REST API docs](https://www.ory.sh/docs/api/oathkeeper/) + +### Rules CLI API + +Management of rules is not only possible through the REST API, but additionally using the ORY Oathkeeper CLI. +For help on how to manage the CLI, type `oathkeeper help rules`. diff --git a/guides/oathkeeper/1-overview/2-installation.md b/_legacy/_master/guides/oathkeeper/1-overview/2-installation.md similarity index 100% rename from guides/oathkeeper/1-overview/2-installation.md rename to _legacy/_master/guides/oathkeeper/1-overview/2-installation.md diff --git a/guides/oathkeeper/1-overview/3-configuration.md b/_legacy/_master/guides/oathkeeper/1-overview/3-configuration.md similarity index 100% rename from guides/oathkeeper/1-overview/3-configuration.md rename to _legacy/_master/guides/oathkeeper/1-overview/3-configuration.md diff --git a/guides/oathkeeper/1-overview/4-deployment.md b/_legacy/_master/guides/oathkeeper/1-overview/4-deployment.md similarity index 100% rename from guides/oathkeeper/1-overview/4-deployment.md rename to _legacy/_master/guides/oathkeeper/1-overview/4-deployment.md diff --git a/guides/oathkeeper/1-overview/5-proxy-judge.md b/_legacy/_master/guides/oathkeeper/1-overview/5-proxy-judge.md similarity index 100% rename from guides/oathkeeper/1-overview/5-proxy-judge.md rename to _legacy/_master/guides/oathkeeper/1-overview/5-proxy-judge.md diff --git a/guides/oathkeeper/1-overview/README.md b/_legacy/_master/guides/oathkeeper/1-overview/README.md similarity index 100% rename from guides/oathkeeper/1-overview/README.md rename to _legacy/_master/guides/oathkeeper/1-overview/README.md diff --git a/guides/oathkeeper/README.md b/_legacy/_master/guides/oathkeeper/README.md similarity index 100% rename from guides/oathkeeper/README.md rename to _legacy/_master/guides/oathkeeper/README.md diff --git a/guides/oathkeeper/diagrams/api_router_layout.mermaid b/_legacy/_master/guides/oathkeeper/diagrams/api_router_layout.mermaid similarity index 100% rename from guides/oathkeeper/diagrams/api_router_layout.mermaid rename to _legacy/_master/guides/oathkeeper/diagrams/api_router_layout.mermaid diff --git a/guides/oathkeeper/diagrams/invalid_access_request.mermaid b/_legacy/_master/guides/oathkeeper/diagrams/invalid_access_request.mermaid similarity index 100% rename from guides/oathkeeper/diagrams/invalid_access_request.mermaid rename to _legacy/_master/guides/oathkeeper/diagrams/invalid_access_request.mermaid diff --git a/guides/oathkeeper/diagrams/sidecar_layout.mermaid b/_legacy/_master/guides/oathkeeper/diagrams/sidecar_layout.mermaid similarity index 100% rename from guides/oathkeeper/diagrams/sidecar_layout.mermaid rename to _legacy/_master/guides/oathkeeper/diagrams/sidecar_layout.mermaid diff --git a/guides/oathkeeper/diagrams/valid_access_request.mermaid b/_legacy/_master/guides/oathkeeper/diagrams/valid_access_request.mermaid similarity index 100% rename from guides/oathkeeper/diagrams/valid_access_request.mermaid rename to _legacy/_master/guides/oathkeeper/diagrams/valid_access_request.mermaid diff --git a/guides/oathkeeper/images/api_router_layout.svg b/_legacy/_master/guides/oathkeeper/images/api_router_layout.svg similarity index 100% rename from guides/oathkeeper/images/api_router_layout.svg rename to _legacy/_master/guides/oathkeeper/images/api_router_layout.svg diff --git a/guides/oathkeeper/images/gateway_deployment.svg b/_legacy/_master/guides/oathkeeper/images/gateway_deployment.svg similarity index 100% rename from guides/oathkeeper/images/gateway_deployment.svg rename to _legacy/_master/guides/oathkeeper/images/gateway_deployment.svg diff --git a/guides/oathkeeper/images/id_token.svg b/_legacy/_master/guides/oathkeeper/images/id_token.svg similarity index 100% rename from guides/oathkeeper/images/id_token.svg rename to _legacy/_master/guides/oathkeeper/images/id_token.svg diff --git a/guides/oathkeeper/images/insecure-connection.jpg b/_legacy/_master/guides/oathkeeper/images/insecure-connection.jpg similarity index 100% rename from guides/oathkeeper/images/insecure-connection.jpg rename to _legacy/_master/guides/oathkeeper/images/insecure-connection.jpg diff --git a/guides/oathkeeper/images/invalid_access_request.svg b/_legacy/_master/guides/oathkeeper/images/invalid_access_request.svg similarity index 100% rename from guides/oathkeeper/images/invalid_access_request.svg rename to _legacy/_master/guides/oathkeeper/images/invalid_access_request.svg diff --git a/guides/oathkeeper/images/sidecar_deployment.svg b/_legacy/_master/guides/oathkeeper/images/sidecar_deployment.svg similarity index 100% rename from guides/oathkeeper/images/sidecar_deployment.svg rename to _legacy/_master/guides/oathkeeper/images/sidecar_deployment.svg diff --git a/guides/oathkeeper/images/valid_access_request.svg b/_legacy/_master/guides/oathkeeper/images/valid_access_request.svg similarity index 100% rename from guides/oathkeeper/images/valid_access_request.svg rename to _legacy/_master/guides/oathkeeper/images/valid_access_request.svg diff --git a/guides/package.json b/_legacy/_master/guides/package.json similarity index 100% rename from guides/package.json rename to _legacy/_master/guides/package.json diff --git a/_legacy/_master/guides/performance/1-hydra.md b/_legacy/_master/guides/performance/1-hydra.md new file mode 100644 index 000000000..6fa593785 --- /dev/null +++ b/_legacy/_master/guides/performance/1-hydra.md @@ -0,0 +1,244 @@ +# ORY Hydra Performance Benchmarks + +In this document you will find benchmark results for different endpoints of ORY Hydra. All benchmarks are executed +using [rakyll/hey](https://github.com/rakyll/hey). Please note that these benchmarks run against the in-memory storage +adapter of ORY Hydra. These benchmarks represent what performance you would get with a zero-overhead database implementation. + +We do not include benchmarks against databases (e.g. MySQL or PostgreSQL) as the performance greatly differs between +deployments (e.g. request latency, database configuration) and tweaking individual things may greatly improve performance. +We believe, for that reason, that benchmark results for these database adapters are difficult to generalize and potentially +deceiving. They are thus not included. + +This file is updated on every push to master. It thus represents the benchmark data for the latest version. + +All benchmarks run 10.000 requests in total, with 100 concurrent requests. All benchmarks run on Circle-CI with a +["2 CPU cores and 4GB RAM"](https://support.circleci.com/hc/en-us/articles/360000489307-Why-do-my-tests-take-longer-to-run-on-CircleCI-than-locally-) +configuration. + +## BCrypt + +ORY Hydra uses BCrypt to obfuscate secrets of OAuth 2.0 Clients. When using flows such as the OAuth 2.0 Client Credentials +Grant, ORY Hydra validates the client credentials using BCrypt which causes (by design) CPU load. CPU load and performance +depend on the BCrypt cost which can be set using the environment variable `BCRYPT_COST`. For these benchmarks, +we have set `BCRYPT_COST=8`. + +## OAuth 2.0 + +This section contains various benchmarks against OAuth 2.0 endpoints + +### Token Introspection + +``` + +Summary: + Total: 0.5157 secs + Slowest: 0.0285 secs + Fastest: 0.0001 secs + Average: 0.0049 secs + Requests/sec: 19392.2046 + + Total data: 1550000 bytes + Size/request: 155 bytes + +Response time histogram: + 0.000 [1] | + 0.003 [4004] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.006 [2006] |■■■■■■■■■■■■■■■■■■■■ + 0.009 [2091] |■■■■■■■■■■■■■■■■■■■■■ + 0.011 [1045] |■■■■■■■■■■ + 0.014 [538] |■■■■■ + 0.017 [211] |■■ + 0.020 [56] |■ + 0.023 [20] | + 0.026 [23] | + 0.028 [5] | + + +Latency distribution: + 10% in 0.0001 secs + 25% in 0.0002 secs + 50% in 0.0044 secs + 75% in 0.0077 secs + 90% in 0.0108 secs + 95% in 0.0133 secs + 99% in 0.0172 secs + +Details (average, fastest, slowest): + DNS+dialup: 0.0000 secs, 0.0001 secs, 0.0285 secs + DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0047 secs + req write: 0.0001 secs, 0.0000 secs, 0.0080 secs + resp wait: 0.0046 secs, 0.0001 secs, 0.0222 secs + resp read: 0.0001 secs, 0.0000 secs, 0.0076 secs + +Status code distribution: + [200] 10000 responses + + + +``` + +### Client Credentials Grant + +This endpoint uses [BCrypt](#bcrypt). + +``` + +Summary: + Total: 18.3913 secs + Slowest: 0.7926 secs + Fastest: 0.0166 secs + Average: 0.1775 secs + Requests/sec: 543.7344 + + Total data: 1570000 bytes + Size/request: 157 bytes + +Response time histogram: + 0.017 [1] | + 0.094 [1780] |■■■■■■■■■■■■■■■■■■■■■■ + 0.172 [2985] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.249 [3277] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.327 [1395] |■■■■■■■■■■■■■■■■■ + 0.405 [406] |■■■■■ + 0.482 [93] |■ + 0.560 [43] |■ + 0.637 [16] | + 0.715 [3] | + 0.793 [1] | + + +Latency distribution: + 10% in 0.0763 secs + 25% in 0.1070 secs + 50% in 0.1782 secs + 75% in 0.2209 secs + 90% in 0.3015 secs + 95% in 0.3438 secs + 99% in 0.4383 secs + +Details (average, fastest, slowest): + DNS+dialup: 0.0000 secs, 0.0166 secs, 0.7926 secs + DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0061 secs + req write: 0.0001 secs, 0.0000 secs, 0.0771 secs + resp wait: 0.1769 secs, 0.0165 secs, 0.7925 secs + resp read: 0.0004 secs, 0.0000 secs, 0.0700 secs + +Status code distribution: + [200] 10000 responses + + + +``` + +## OAuth 2.0 Client Management + +### Creating OAuth 2.0 Clients + +This endpoint uses [BCrypt](#bcrypt) and generates IDs and secrets by reading from which negatively impacts +performance. Performance will be better if IDs and secrets are set in the request as opposed to generated by ORY Hydra. + +``` +This test is currently disabled due to issues with /dev/urandom being inaccessible in the CI. +``` + +### Listing OAuth 2.0 Clients + +``` + +Summary: + Total: 0.5373 secs + Slowest: 0.0514 secs + Fastest: 0.0001 secs + Average: 0.0051 secs + Requests/sec: 18610.8466 + + Total data: 4370000 bytes + Size/request: 437 bytes + +Response time histogram: + 0.000 [1] | + 0.005 [6136] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.010 [2148] |■■■■■■■■■■■■■■ + 0.015 [1135] |■■■■■■■ + 0.021 [322] |■■ + 0.026 [145] |■ + 0.031 [17] | + 0.036 [44] | + 0.041 [14] | + 0.046 [2] | + 0.051 [36] | + + +Latency distribution: + 10% in 0.0001 secs + 25% in 0.0002 secs + 50% in 0.0033 secs + 75% in 0.0080 secs + 90% in 0.0126 secs + 95% in 0.0163 secs + 99% in 0.0274 secs + +Details (average, fastest, slowest): + DNS+dialup: 0.0000 secs, 0.0001 secs, 0.0514 secs + DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0336 secs + req write: 0.0001 secs, 0.0000 secs, 0.0361 secs + resp wait: 0.0043 secs, 0.0000 secs, 0.0274 secs + resp read: 0.0006 secs, 0.0000 secs, 0.0406 secs + +Status code distribution: + [200] 10000 responses + + + +``` + +### Fetching a specific OAuth 2.0 Client + +``` + +Summary: + Total: 0.5320 secs + Slowest: 0.0371 secs + Fastest: 0.0001 secs + Average: 0.0050 secs + Requests/sec: 18796.1670 + + Total data: 4350000 bytes + Size/request: 435 bytes + +Response time histogram: + 0.000 [1] | + 0.004 [5214] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.007 [2019] |■■■■■■■■■■■■■■■ + 0.011 [1371] |■■■■■■■■■■■ + 0.015 [678] |■■■■■ + 0.019 [296] |■■ + 0.022 [257] |■■ + 0.026 [68] |■ + 0.030 [76] |■ + 0.033 [12] | + 0.037 [8] | + + +Latency distribution: + 10% in 0.0001 secs + 25% in 0.0002 secs + 50% in 0.0033 secs + 75% in 0.0079 secs + 90% in 0.0125 secs + 95% in 0.0169 secs + 99% in 0.0258 secs + +Details (average, fastest, slowest): + DNS+dialup: 0.0000 secs, 0.0001 secs, 0.0371 secs + DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0053 secs + req write: 0.0001 secs, 0.0000 secs, 0.0161 secs + resp wait: 0.0042 secs, 0.0000 secs, 0.0352 secs + resp read: 0.0005 secs, 0.0000 secs, 0.0179 secs + +Status code distribution: + [200] 10000 responses + + + +``` diff --git a/guides/performance/README.md b/_legacy/_master/guides/performance/README.md similarity index 100% rename from guides/performance/README.md rename to _legacy/_master/guides/performance/README.md diff --git a/guides/telemetry/README.md b/_legacy/_master/guides/telemetry/README.md similarity index 100% rename from guides/telemetry/README.md rename to _legacy/_master/guides/telemetry/README.md diff --git a/guides/ecosystem/3-upgrading.md b/_legacy/_master/guides/updating/README.md similarity index 100% rename from guides/ecosystem/3-upgrading.md rename to _legacy/_master/guides/updating/README.md diff --git a/guides/yarn.lock b/_legacy/_master/guides/yarn.lock similarity index 100% rename from guides/yarn.lock rename to _legacy/_master/guides/yarn.lock diff --git a/package.json b/_legacy/_master/package.json similarity index 100% rename from package.json rename to _legacy/_master/package.json diff --git a/yarn.lock b/_legacy/_master/yarn.lock similarity index 100% rename from yarn.lock rename to _legacy/_master/yarn.lock diff --git a/_legacy/faq/README.md b/_legacy/faq/README.md new file mode 100644 index 000000000..397e1f723 --- /dev/null +++ b/_legacy/faq/README.md @@ -0,0 +1 @@ +Welcome to the ORY FAQ. You will find frequently asked questions and their answers in this section. \ No newline at end of file diff --git a/_legacy/faq/SUMMARY.md b/_legacy/faq/SUMMARY.md new file mode 100644 index 000000000..30aa46d24 --- /dev/null +++ b/_legacy/faq/SUMMARY.md @@ -0,0 +1,11 @@ +# Summary + +* [How can I modify the lifespan of access tokens, refresh tokens, ID tokens, and authorize codes?](hydra/lifespan.md) +* [How do environment variables work in general and especially in docker?](general/envs.md) +* [What's the difference between OAuth 2.0 Scopes and Access Control Policies?](hydra/scope-acp.md) +* [I am new to OAuth 2.0. Where can I find good tutorials?](hydra/oauth2-tutorials.md) +* [Do you offer consulting services?](general/consulting.md) +* [Will this technology be open source forever?](general/open-source.md) +* [Do you have job openings?](general/jobs.md) +* [Do you support JSON Web Tokens (JWTs)?](general/jwt.md) +* [Where is the documentation?](general/docs.md) diff --git a/_legacy/faq/_layouts/website/page.html b/_legacy/faq/_layouts/website/page.html new file mode 100644 index 000000000..7dd499e5d --- /dev/null +++ b/_legacy/faq/_layouts/website/page.html @@ -0,0 +1,24 @@ +{% extends template.self %} + +{% block faq_header_brand %} + +{% endblock %} + +{% block faq_menu %} + +{% endblock %} + +{% block faq_footer %} + + + +
+{% endblock %} diff --git a/_legacy/faq/book.json b/_legacy/faq/book.json new file mode 100644 index 000000000..fb671d23c --- /dev/null +++ b/_legacy/faq/book.json @@ -0,0 +1,23 @@ +{ + "plugins": [ + "theme-faq", + "-fontsettings", + "-sharing", + "sitemap-basepath", + "edit-link", + "ga" + ], + "pluginsConfig": { + "edit-link": { + "base": "https://github.com/ory/docs/tree/master/faq", + "label": "Edit this page" + }, + "ga": { + "token": "UA-71865250-1" + }, + "sitemap-basepath": { + "hostname": "https://www.ory.sh/", + "basePath": "/docs/faq/" + } + } +} \ No newline at end of file diff --git a/_legacy/faq/general/consulting.md b/_legacy/faq/general/consulting.md new file mode 100644 index 000000000..7a0651e50 --- /dev/null +++ b/_legacy/faq/general/consulting.md @@ -0,0 +1 @@ +Yes we do. Drop us a line at [hi@ory.am](mailto:hi@ory.am)! \ No newline at end of file diff --git a/_legacy/faq/general/docs.md b/_legacy/faq/general/docs.md new file mode 100644 index 000000000..6b58fbbb2 --- /dev/null +++ b/_legacy/faq/general/docs.md @@ -0,0 +1 @@ +The documentation is available at [www.ory.sh/docs](https://www.ory.sh/docs). diff --git a/_legacy/faq/general/envs.md b/_legacy/faq/general/envs.md new file mode 100644 index 000000000..47587836b --- /dev/null +++ b/_legacy/faq/general/envs.md @@ -0,0 +1,3 @@ +For general information on environment variables, check out [this article](https://en.wikipedia.org/wiki/Environment_variable). + +For information on using environment variables in docker, check out [this Stack Overflow question](https://stackoverflow.com/questions/30494050/how-do-i-pass-environment-variables-to-docker-containers). diff --git a/_legacy/faq/general/jobs.md b/_legacy/faq/general/jobs.md new file mode 100644 index 000000000..c17b83010 --- /dev/null +++ b/_legacy/faq/general/jobs.md @@ -0,0 +1,3 @@ +We are always looking for talented software engineers. You should have solid experience with Go and NodeJS. You should have +contributed to an open source project and have skills in containerization, CI/CD and generally running software in the cloud. +If you think you could fit the profile, drop us a line at [hi@ory.am](mailto:hi@ory.am). diff --git a/_legacy/faq/general/jwt.md b/_legacy/faq/general/jwt.md new file mode 100644 index 000000000..04cc5c11c --- /dev/null +++ b/_legacy/faq/general/jwt.md @@ -0,0 +1,20 @@ +JSON Web Tokens make much sense, when a client needs to verify the payload of a token. For example, OpenID Connect ID Tokens +are consumed by a client. The payload of the ID token is used to authenticate the user in the client app. + +The payloads of OAuth2 access and refresh tokens however are not relevant to the client. The payload is only +relevant to the protected resource (e.g. an image), as that resource must check if the token is allowed to access it. +The payload of the token is only relevant to the resource provider and the OAuth2 server. Therefore, JSON Web Tokens +are the wrong tool for this task. + +Additionally, using JSON Web Tokens for access and refresh tokens adds another limitation. It becomes impossible to +blacklist the tokens. They are valid as long as their signature - and the corresponding private key - is valid and as long +as they are not expired. While it is theoretically possible to introduce a blacklist and "ban" certain tokens, it defeats +the stateless nature of JSON Web Tokens as it would be necessary to check if the token is on the blacklist on every request. + +For these and other reasons, ORY Hydra does not issue JSON Web Tokens as access and refresh tokens, and never will. + +There is however one caveat to this story. Can you protect your API endpoints without making a REST call to ORY Hydra +on every access request? With ORY Oathkeeper, you can! + +[ORY Oathkeeper](https://github.com/ory/oathkeeper) is a HTTP Reverse Proxy which "translates" ORY Hydra's access tokens to stateless JSON Web Tokens. It +basically replaces the access token with a JSON Web Token which can be used as a stateless session identifier in your API endpoint. diff --git a/_legacy/faq/general/open-source.md b/_legacy/faq/general/open-source.md new file mode 100644 index 000000000..fa15caba0 --- /dev/null +++ b/_legacy/faq/general/open-source.md @@ -0,0 +1 @@ +Yes. \ No newline at end of file diff --git a/_legacy/faq/hydra/lifespan.md b/_legacy/faq/hydra/lifespan.md new file mode 100644 index 000000000..c54cf2396 --- /dev/null +++ b/_legacy/faq/hydra/lifespan.md @@ -0,0 +1 @@ +Use `hydra help host | grep LIFESPAN` to get a list of options allowing you to customize the lifespan of tokens. \ No newline at end of file diff --git a/_legacy/faq/hydra/oauth2-tutorials.md b/_legacy/faq/hydra/oauth2-tutorials.md new file mode 100644 index 000000000..fde88ba54 --- /dev/null +++ b/_legacy/faq/hydra/oauth2-tutorials.md @@ -0,0 +1,4 @@ +Here are some read-worthy resources on OAuth 2.0: + +* [A quick guide to OAuth 2.0 by Digital Ocean](https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2) +* [A broad overview of OAuth 2.0 written by Aaron Parecki](https://www.oauth.com/oauth2-servers/oauth2-clients/server-side-apps/) diff --git a/_legacy/faq/hydra/scope-acp.md b/_legacy/faq/hydra/scope-acp.md new file mode 100644 index 000000000..931983fc2 --- /dev/null +++ b/_legacy/faq/hydra/scope-acp.md @@ -0,0 +1,12 @@ +OAuth 2.0 scopes and Access Control Policies cover different aspects of access control. OAuth 2.0 scopes do not represent +what a resource owner ("user") is able to do in a system or not. They do not express things like administrative rights. + +OAuth 2.0 scopes express what a user allowed a token to do on his/her behalf. For example, an access token might be allowed +to see a user's pictures, but not upload new pictures on his/her behalf. The user him/herself however is generally allowed +to view and upload pictures. OAuth 2.0 scopes do not express a user's permissions. They express what an OAuth 2.0 Client +may do on the user's behalf - independently of whether or not the user is actually allowed to do that. + +Access Control Policies say what permissions a user has. For example, a user may be allowed to upload and view pictures, +but not ban other users. The latter permission is only allowed if the user is an administrator. Access Control Policies +cover use cases where you would use RBAC or ACL - but are generally more powerful. A user may be an administrator and thus +allowed to ban user, but the user might choose to not grant this capability (OAuth 2.0 scope) to some OAuth 2.0 Client. \ No newline at end of file diff --git a/_legacy/faq/package.json b/_legacy/faq/package.json new file mode 100644 index 000000000..9664f260a --- /dev/null +++ b/_legacy/faq/package.json @@ -0,0 +1,4 @@ +{ + "dependencies": { + } +} diff --git a/_legacy/faq/yarn.lock b/_legacy/faq/yarn.lock new file mode 100644 index 000000000..fb57ccd13 --- /dev/null +++ b/_legacy/faq/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + diff --git a/guides/README.md b/_legacy/guides/README.md similarity index 100% rename from guides/README.md rename to _legacy/guides/README.md diff --git a/guides/SUMMARY.md b/_legacy/guides/SUMMARY.md similarity index 100% rename from guides/SUMMARY.md rename to _legacy/guides/SUMMARY.md diff --git a/_legacy/guides/_layouts/website/page.html b/_legacy/guides/_layouts/website/page.html new file mode 100644 index 000000000..a30e4825e --- /dev/null +++ b/_legacy/guides/_layouts/website/page.html @@ -0,0 +1,7 @@ +{% extends template.self %} + +{% block body %} + {{ super() }} + +
+{% endblock %} diff --git a/guides/book.json b/_legacy/guides/book.json similarity index 100% rename from guides/book.json rename to _legacy/guides/book.json diff --git a/guides/ecosystem/0-overview.md b/_legacy/guides/ecosystem/0-overview.md similarity index 100% rename from guides/ecosystem/0-overview.md rename to _legacy/guides/ecosystem/0-overview.md diff --git a/guides/ecosystem/1-cloud-native.md b/_legacy/guides/ecosystem/1-cloud-native.md similarity index 100% rename from guides/ecosystem/1-cloud-native.md rename to _legacy/guides/ecosystem/1-cloud-native.md diff --git a/guides/ecosystem/2-versions.md b/_legacy/guides/ecosystem/2-versions.md similarity index 100% rename from guides/ecosystem/2-versions.md rename to _legacy/guides/ecosystem/2-versions.md diff --git a/_legacy/guides/ecosystem/3-upgrading.md b/_legacy/guides/ecosystem/3-upgrading.md new file mode 100644 index 000000000..970bed824 --- /dev/null +++ b/_legacy/guides/ecosystem/3-upgrading.md @@ -0,0 +1,15 @@ +# Performing Software Updates + +Good software improves over time. If it wouldn't, you shouldn't use it. Unfortunately, some of these improvements +have breaking changes. We know that breaking changes are annoying so we want to make upgrading as painless as possible. + +We document detailed changelogs and upgrade guides for this very reason: + +* ORY Hydra: [upgrade guide](https://github.com/ory/hydra/blob/master/UPGRADE.md), [changelog](https://github.com/ory/hydra/blob/master/CHANGELOG.md) +* ORY Oathkeeper: [upgrade guide](https://github.com/ory/oathkeeper/blob/master/UPGRADE.md), [changelog](https://github.com/ory/oathkeeper/blob/master/CHANGELOG.md) +* ORY Keto: [upgrade guide](https://github.com/ory/keto/blob/master/UPGRADE.md), [changelog](https://github.com/ory/keto/blob/master/CHANGELOG.md) + +Before upgrading to a newer version, please make sure to check with these documents first. + +**If you have not already subscribed to our release announcements, [subscribe now](http://eepurl.com/di390P)!** We announce +important releases (e.g. security releases) in this newsletter. diff --git a/guides/ecosystem/4-examples.md b/_legacy/guides/ecosystem/4-examples.md similarity index 100% rename from guides/ecosystem/4-examples.md rename to _legacy/guides/ecosystem/4-examples.md diff --git a/_legacy/guides/hydra/1-tutorial/README.md b/_legacy/guides/hydra/1-tutorial/README.md new file mode 100644 index 000000000..5abc9713e --- /dev/null +++ b/_legacy/guides/hydra/1-tutorial/README.md @@ -0,0 +1,133 @@ +# 5 Minute Tutorial + +To get started quickly, we provide a Docker Compose based example for setting up ORY Hydra, a PostgreSQL instance +and an exemplary user login & consent app. You need to have the latest Docker as well as Docker Compose version installed. + +OAuth2 Flow + +Next, clone (`git clone https://github.com/ory/hydra.git`), [download](https://github.com/ory-am/hydra/archive/master.zip), +or use `go get -d github.com/ory/hydra` - if you have Go (1.10+) installed on you system - to download the Docker Compose +set up. + +``` +$ git clone https://github.com/ory/hydra.git +$ cd hydra +$ git checkout tags/v1.0.0-beta.8 + +$ docker-compose -p hydra up --build +Starting hydra_mysqld_1 +Starting hydra_postgresd_1 +Starting hydra_hydra_1 + +[...] +``` + + +Everything should running now! Let's confirm that everything is working by creating our first OAuth 2.0 Client. +The following commands will use Docker wizardry. You can obviously install the ORY Hydra CLI locally and avoid using +Docker here. If you do use the CLI locally, you can omit `docker exec -it hydra_hydra_1 \` completely. + +You will notice that two ports are being used. Port `4444` and port `4445`. The former is for request to ORY Hydra's public +endpoints. The latter to its administrative endpoints. For more information on this, head over to +[Securing ORY Hydra](../2-environment/securing-ory-hydra.md). If you want to run ORY Hydra admin and +public services in two separate containers, run + +``` +$ docker-compose -p hydra -f docker-compose-twoc.yml up --build +``` + +Please be aware that you will not be able to run the hydra CLI from within docker if you +use the docker-compose-twoc.yml file. Instead, you must install the CLI locally and +omit `docker exec -it hydra_hydra_1 \` from your commands. + +Ok, let's continue by creating a new OAuth 2.0 Client. + +``` +# Creates a new OAuth 2.0 client +$ docker exec -it hydra_hydra_1 \ + hydra clients create \ + --endpoint http://localhost:4445 \ + --id my-client \ + --secret secret \ + -g client_credentials + +OAuth2 client id: my-client +OAuth2 client secret: secret + +# Let's perform the client credentials grant. +$ docker exec -it hydra_hydra_1 \ + hydra token client \ + --endpoint http://localhost:4444 \ + --client-id my-client \ + --client-secret secret + +UDYMha9TwsMBejEvKfnDOXkhgkLsnmUNYVQDklT5bD8.ZNpuNRC85erbIYDjPqhMwTinlvQmNTk_UvttcLQxFJY + +# Let's perform token introspection on that token. Make sure to copy the token you just got and not the dummy value. +$ docker exec -it hydra_hydra_1 \ + hydra token introspect \ + --endpoint http://localhost:4445 \ + --client-id my-client \ + --client-secret secret \ + UDYMha9TwsMBejEvKfnDOXkhgkLsnmUNYVQDklT5bD8.ZNpuNRC85erbIYDjPqhMwTinlvQmNTk_UvttcLQxFJY + +{ + "active": true, + "client_id": "my-client", + "exp": 1527078658, + "iat": 1527075058, + "iss": "http://localhost:4444", + "sub": "my-client", + "token_type": "access_token" +} +``` + +Next, we will perform the OAuth 2.0 Authorization Code Grant. For that, we must first create a client that is capable +of performing that grant: + +``` +$ docker exec -it hydra_hydra_1 \ + hydra clients create \ + --endpoint http://localhost:4445 \ + --id auth-code-client \ + --secret secret \ + --grant-types authorization_code,refresh_token \ + --response-types code,id_token \ + --scope openid,offline \ + --callbacks http://127.0.0.1:5555/callback +``` + +Note that you need to add `--token-endpoint-auth-method none` if your clients are public (such as SPA apps and native apps) because the public clients could not provide client secret. + + +The next command starts a server that serves an example web application. The application will perform the OAuth 2.0 +Authorization Code Flow using ORY Hydra. The web server runs on [http://127.0.0.1:5555](http://127.0.0.1:5555). + +``` +$ docker exec -it hydra_hydra_1 \ + hydra token user \ + --client-id auth-code-client \ + --client-secret secret \ + --endpoint http://localhost:4444/ \ + --port 5555 \ + --scope openid,offline + +Setting up home route on http://127.0.0.1:4445/ +Setting up callback listener on http://127.0.0.1:4445/callback +Press ctrl + c on Linux / Windows or cmd + c on OSX to end the process. +If your browser does not open automatically, navigate to: + + http://127.0.0.1:5555/ +``` + +Open the URL [http://127.0.0.1:5555/](http://127.0.0.1:5555/), log in, and authorize the application. Next, you should +see at least an access token in the response. If you granted the `offline` scope, you will also see a refresh token. +If you granted the `openid` scope, you will get an ID Token as well. + +Great! You installed hydra, connected the CLI, created a client and completed two authentication flows! +Before you continue, clean up this set up in order to avoid conflicts with other tutorials form this guide: + +``` +$ docker-compose kill +$ docker-compose rm -f +``` diff --git a/_legacy/guides/hydra/2-environment/1-securing-ory-hydra.md b/_legacy/guides/hydra/2-environment/1-securing-ory-hydra.md new file mode 100644 index 000000000..121a3ce5a --- /dev/null +++ b/_legacy/guides/hydra/2-environment/1-securing-ory-hydra.md @@ -0,0 +1,34 @@ +# Securing ORY Hydra + +ORY Hydra exposes serves APIs via two ports: + +- Public port (default 4444) +- Administrative port (default 4445) + +The public port can and should be exposed to public internet traffic. That port handles requests to: + +* `./well-known/jwks.json` +* `./well-known/openid-configuration` +* `/oauth2/auth` +* `/oauth2/token` +* `/oauth2/revoke` +* `/oauth2/fallbacks/consent` +* `/oauth2/fallbacks/error` +* `/userinfo` + +The administrative port should not be exposed to public internet traffic. If you want to expose certain endpoints, such as the `/clients` endpoint for +OpenID Connect Dynamic Client Registry, you can do so but you need to properly secure these endpoints with an API Gateway or Authorization Proxy. +Administrative endpoints include: + +* All `/clients` endpoints. +* All `/jwks` endpoints. +* All `/health`, `/metrics`, `/version` endpoints. +* All `/oauth2/auth/requests` endpoints. +* Endpoint `/oauth2/introspect`. +* Endpoint `/oauth2/flush`. + +None of the administrative endpoints have any built-in access control. You can do simple `curl` or Postman requests to talk to them. + +We generally advise to run ORY Hydra with `hydra serve all` which listens on both ports in one process. If you wish to have more granular control over +each endpoint's settings (e.g. CORS), you can run `hydra serve admin` and `hydra serve public` separately. Please be aware that the `memory` backend +will not work in this mode. diff --git a/_legacy/guides/hydra/2-environment/README.md b/_legacy/guides/hydra/2-environment/README.md new file mode 100644 index 000000000..d31f651ef --- /dev/null +++ b/_legacy/guides/hydra/2-environment/README.md @@ -0,0 +1,64 @@ +# Dependencies & Environment + + + +ORY Hydra is built cloud native and implements [12factor](http://12factor.net) principles. The Docker Image is 5 MB light +and versioned with [verbose upgrade instructions](https://github.com/ory/hydra/blob/master/UPGRADE.md) +and [detailed changelogs](https://github.com/ory/hydra/blob/master/CHANGELOG.md). Auto-scaling, migrations, health checks, +it all works with zero additional work required. It is possible to run ORY Hydra on any platform, including but not limited +to OSX, Linux, Windows, ARM, FreeBSD and more. + +ORY Hydra has two operational modes: + +* In-memory: This mode does not work with more than one instance ("cluster") and any state is lost after restarting the instance. +* SQL: This mode works with more than one instance and state is not lost after restarts. + +No further dependencies are required for a production-ready instance. + +## SQL + +The SQL adapter supports two DBMS: PostgreSQL 9.6+ and MySQL 5.7+. Please note that +older MySQL versions have issues with ORY Hydra's database schema. For more information [go here](https://github.com/ory/hydra/issues/377). + +If you do run the SQL adapter, you must first create the database schema. The `hydra serve` command does not do this +automatically, instead you must run `hydra migrate sql` to create the schemas. The `hydra migrate sql` command +also runs database migrations in case of an upgrade. Please follow the [upgrade instructions](https://github.com/ory/hydra/blob/master/UPGRADE.md) +to see when you need to run this command. Always create a backup before running `hydra migrate sql`! + +Running SQL migrations in Docker is very easy, check out the [docker-compose](https://github.com/ory/hydra/blob/master/docker-compose.yml) +example to see how we did it! + +### Configuration + +Both MySQL and PostgreSQL adapters support the following settings. You can modify these settings by appending query parameters to your DSN (`postgres://user:pw@host:port/database?setting1=foo&setting2=bar`): + +* `max_conns` sets the maximum number of open connections to the database. Defaults to the number of CPUs. Example `postgres://user:pw@host:port/database?max_conns=10`. +* `max_idle_conns` sets the maximum number of connections in the idle connection pool. Defaults to the number of CPUs. Example `postgres://user:pw@host:port/database?max_idle_conns=5`. +* `max_conn_lifetime` sets the maximum amount of time (`ms`, `s`, `m`, `h`) a connection may be reused. Defaults to 0. Example `postgres://user:pw@host:port/database?max_conn_lifetime=10s`. + +#### MySQL + +On top of the settings above, MySQL supports additional settings: + +* `sql_notes`, if set to `false`, ignores MySQL notices. If left empty or set to `true`, they will be treated as warnings. Example `mysql://user:pw@host:port/database?sql_notes=false`. +* `sql_mode` sets the server-side strict mode. Read more about possible values [here](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html). + +#### PostgreSQL + +On top of the settings above, PostgreSQL supports additional settings: + +* `sslmode` sets whether or not to use SSL (default is require, this is not the default for libpq). Valid values for sslmode are: + * `disable` - No SSL + * `require` - Always SSL (skip verification) + * `verify-ca` - Always SSL (verify that the certificate presented by the + server was signed by a trusted CA) + * `verify-full` - Always SSL (verify that the certification presented by + the server was signed by a trusted CA and the server host name + matches the one in the certificate) +* `fallback_application_name` - An application_name to fall back to if one isn't provided. +* `connect_timeout` - Maximum wait for connection, in seconds. Zero or +not specified means wait indefinitely. +* `sslcert` - Cert file location. The file must contain PEM encoded data. +* `sslkey` - Key file location. The file must contain PEM encoded data. +* `sslrootcert` - The location of the root certificate file. The file +must contain PEM encoded data. \ No newline at end of file diff --git a/_legacy/guides/hydra/3-overview/1-oauth2.md b/_legacy/guides/hydra/3-overview/1-oauth2.md new file mode 100644 index 000000000..297a8fe48 --- /dev/null +++ b/_legacy/guides/hydra/3-overview/1-oauth2.md @@ -0,0 +1,392 @@ +# OAuth 2.0 & OpenID Connect + +ORY Hydra's primary feature is implementing the OAuth 2.0 and OpenID Connect spec, as well as related specs by the IETF +and OpenID Foundation. + +This section explains how to connect your existing user management (user login, registration, logout, ...) with ORY Hydra +in order to become an OAuth 2.0 and OpenID Connect provider like Google, Dropbox, or Facebook. + +Please be aware that you must know how OAuth 2.0 and OpenID Connect work. This documentation will not teach you how +these protocols work. + + + +## Glossary + +Before we get into the gritty details of how everything fits together, let's get some terminologies out of the way. You will +find these terminologies scattered across the OAuth2 and OpenID Connect ecosystem. + +We decided, for this guide, to use simpler and easier to use terminologies like, for example, *user* instead of *resource owner*. +If you are familiar with OAuth2 details, you will find it easier to navigate these docs if you have read the glossary. + +1. The **resource owner** is the user who authorizes an application to access their account. The application's access to +the user's account is limited to the "scope" of the authorization granted (e.g. read or write access). We will refer to +the resource owner as a *user* or *end user* on this page. +2. The **OAuth 2.0 Authorization Server** implements the OAuth 2.0 protocol (and optionally OpenID Connect) and serves +endpoints such as `/oauth2/auth` or `/oauth2/token`. In our case, this is **ORY Hydra**. +3. The **resource provider** is a service that - well - provides resources. These resources (e.g. a blog article, printer, todo list) +are owned by a resource owner (user) mentioned above. +3. The **OAuth 2.0 Client** is the *application* that wants access to a resource owner's resources (a.k.a. get write access to a user's images). +Such a client can ask the authorization server to issue an access token on a resource owner's behalf. Typically, the authorization server +will ask the user if he/she "is ok with" giving that application e.g. write access to personal images. +4. The **Identity Provider** is a service ("application"/"website") with a login interface. An identity provider typically +allows users to register as well and might also have an administrative interface in order to manage the identities (delete user, ban user, create user, ...). +5. **User Agent** is usually a browser. +6. **OpenID Connect** is a protocol built on top of OAuth 2.0 which is capable of federating authentication. + +A typical OAuth 2.0 flow looks as follows: + +1. A developer registers an OAuth 2.0 Client at the Authorization Server (ORY Hydra) with the intention of obtaining information on behalf of a user. +2. The application UI asks the user to authorize the application to access information/data on his/her behalf. +3. The user is redirected to the Authorization Server. +4. The Authorization Server confirms the user's identity and asks the user to grant the OAuth 2.0 Client certain permissions. +5. The Authorization Server issues tokens that the OAuth 2.0 client uses to access resources on the user's behalf. + +## Authenticating Users and Requesting Consent + +As you already know by now, ORY Hydra does not come with any type of user management (login, registration, ...). +Instead, it relies on the so-called User Login and Consent Flow. This flow describes a series of redirects where the user's +user agent is redirect to your Login Provider and, once the user is authenticated, to the Consent Provider. The Login +and Consent provider is implemented by you in a programming language of your choice. You could write, for example, a +NodeJS app that handles HTTP requests to `/login` and `/consent` and it would thus be your Login & Consent provider. + +The flow itself works as follows: + +1. The OAuth 2.0 Client initiates an Authorize Code, Hybrid, or Implicit flow. The user's user agent is redirect to +`http://hydra/oauth2/auth?client_id=...&...`. +2. ORY Hydra, if unable to authenticate the user (= no session cookie exists), redirects the user's user agent to the Login Provider +URL. The application "sitting" at that URL is implemented by you and typically shows a login user interface ("Please enter +your username and password"). The URL the user is redirect to looks similar to `http://login-service/login?login_challenge=1234...`. +3. The Login Provider, once the user has successfully logged in, tells ORY Hydra some information about who the user is (e.g. the user's ID) +and also that the login attempt was successful. This is done using a REST request which includes another redirect URL +along the lines of `http://hydra/oauth2/auth?client_id=...&...&login_verifier=4321`. +4. The user's user agent follows the redirect and lands back at ORY Hydra. Next, ORY Hydra redirects the user's user +agent to the Consent Provider, hosted at - for example - `http://consent-service/consent?consent_challenge=4567...` +5. The Consent Provider shows a user interface which asks the user if he/she would like to grant the OAuth 2.0 Client +the requested permissions ("OAuth 2.0 Scope"). You've probably seen this screen around, which is usually something similar to: +*"Would you like to grant Facebook Image Backup access to all your private and public images?"*. +6. The Consent Provider makes another REST request to ORY Hydra to let it know which permissions the user authorized, and +if the user authorized the request at all. The user can usually choose to not grant an application any access to his/her +personal data. In the response of that REST request, a redirect URL is included along the lines of `http://hydra/oauth2/auth?client_id=...&...&consent_verifier=7654...`. +7. The user's user agent follows that redirect. +7. Now, the user has successfully authenticated and authorized the application. Next, ORY Hydra will +run some checks and if everything works out, issue access, refresh, and ID tokens. + +This flow allows you to take full control of the behaviour of your login system (e.g. 2FA, passwordless, ...) and +consent screen. A well-documented reference implementation for both the Login and [Consent Provider is available on GitHub](https://github.com/ory/hydra-login-consent-node). + +### The flow from a user's point of view + +{% youtube %}https://www.youtube.com/watch?v=txUmfORzu8Y{% endyoutube %} + +### The flow from a network perspective + +![https://mermaidjs.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG4gICAgT0F1dGgyIENsaWVudC0-Pk9SWSBIeWRyYTogSW5pdGlhdGVzIE9BdXRoMiBBdXRob3JpemUgQ29kZSBvciBJbXBsaWNpdCBGbG93XG4gICAgT1JZIEh5ZHJhLS0-Pk9SWSBIeWRyYTogTm8gZW5kIHVzZXIgc2Vzc2lvbiBhdmFpbGFibGUgKG5vdCBhdXRoZW50aWNhdGVkKVxuICAgIE9SWSBIeWRyYS0-PkxvZ2luIFByb3ZpZGVyOiBSZWRpcmVjdHMgZW5kIHVzZXIgd2l0aCBsb2dpbiBjaGFsbGVuZ2VcbiAgICBMb2dpbiBQcm92aWRlci0tPk9SWSBIeWRyYTogRmV0Y2hlcyBsb2dpbiBpbmZvXG4gICAgTG9naW4gUHJvdmlkZXItLT4-TG9naW4gUHJvdmlkZXI6IEF1dGhlbnRpY2F0ZXMgdXNlciB3aXRoIGNyZWRlbnRpYWxzXG4gICAgTG9naW4gUHJvdmlkZXItLT5PUlkgSHlkcmE6IFRyYW5zbWl0cyBsb2dpbiBpbmZvIGFuZCByZWNlaXZlcyByZWRpcmVjdCB1cmwgd2l0aCBsb2dpbiB2ZXJpZmllclxuICAgIExvZ2luIFByb3ZpZGVyLT4-T1JZIEh5ZHJhOiBSZWRpcmVjdHMgZW5kIHVzZXIgdG8gcmVkaXJlY3QgdXJsIHdpdGggbG9naW4gdmVyaWZpZXJcbiAgICBPUlkgSHlkcmEtLT4-T1JZIEh5ZHJhOiBGaXJzdCB0aW1lIHRoYXQgY2xpZW50IGFza3MgdXNlciBmb3IgcGVybWlzc2lvbnNcbiAgICBPUlkgSHlkcmEtPj5Db25zZW50IFByb3ZpZGVyOiBSZWRpcmVjdHMgZW5kIHVzZXIgd2l0aCBjb25zZW50IGNoYWxsZW5nZVxuICAgIENvbnNlbnQgUHJvdmlkZXItLT5PUlkgSHlkcmE6IEZldGNoZXMgY29uc2VudCBpbmZvICh3aGljaCB1c2VyLCB3aGF0IGFwcCwgd2hhdCBzY29wZXMpXG4gICAgQ29uc2VudCBQcm92aWRlci0tPj5Db25zZW50IFByb3ZpZGVyOiBBc2tzIGZvciBlbmQgdXNlcidzIHBlcm1pc3Npb24gdG8gZ3JhbnQgYXBwbGljYXRpb24gYWNjZXNzXG4gICAgQ29uc2VudCBQcm92aWRlci0tPk9SWSBIeWRyYTogVHJhbnNtaXRzIGNvbnNlbnQgcmVzdWx0IGFuZCByZWNlaXZlcyByZWRpcmVjdCB1cmwgd2l0aCBjb25zZW50IHZlcmlmaWVyXG4gICAgQ29uc2VudCBQcm92aWRlci0-Pk9SWSBIeWRyYTogUmVkaXJlY3RzIHRvIHJlZGlyZWN0IHVybCB3aXRoIGNvbnNlbnQgdmVyaWZpZXJcbiAgICBPUlkgSHlkcmEtLT4-T1JZIEh5ZHJhOiBWZXJpZmllcyBncmFudFxuICAgIE9SWSBIeWRyYS0-Pk9BdXRoMiBDbGllbnQ6IFRyYW5zbWl0cyBhdXRob3JpemF0aW9uIGNvZGUvdG9rZW4iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ](../images/login-consent-flow.png) + +### Implementing a Login & Consent Provider + +You should now have a high-level idea of how the login and consent providers work. Let's get into the details of it. + +#### OAuth 2.0 Authorize Code Flow + +Before anything happens, the OAuth 2.0 Authorize Code Flow is initiated by an OAuth 2.0 Client. This usually works by +generating a URL in the form of `https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&...`. Then, +the OAuth 2.0 Client points the end user's user agent to that URL. + +Next, the user agent (browser) opens that URL. + +#### User Login + +As the user agent hits the URL, ORY Hydra checks if a session cookie is set containing information about a previously +successful login. Additionally, parameters such as `id_token_hint`, `prompt`, and `max_age` are evaluated and processed. + +Next, the user will be redirect to the Login Provider which was set using the `OAUTH2_LOGIN_URL` environment +variable. For example, the user is redirected to `https://login-provider/login?login_challenge=1234` if `OAUTH2_LOGIN_URL=https://login-provider/login`. +This redirection happens *always* and regardless of whether the user has a valid login session or if the user needs +to authenticate. + +The service which handles requests to `https://login-provider/login` must first fetch information on the authentication +request using a REST API call. Please be aware that for reasons of brevity, the following code snippets are pseudo-code. +For a fully working example, check out our reference [User Login & Consent Provider implementation](https://github.com/ory/hydra-login-consent-node). + +The endpoint handler at `/login` **must not remember previous sessions**. This task is solved by ORY Hydra. If the +REST API call tells you to show the login ui, you **must show it**. If the REST API tells you to not show the login ui, +**you must not show it**. Again, **do not implement any type of session here**. + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +router.get('/login', function (req, res, next) { + challenge = req.url.query.login_challenge; + + fetch('https://hydra/oauth2/auth/requests/login/' + challenge). + then(function (response) { + // ... + }) +}) +``` + +The server response is a JSON object with the following keys: + +``` +{ + // Skip, if true, let's us know that ORY Hydra has successfully authenticated the user and we should not show any UI + "skip": true|false, + + // The user-id of the already authenticated user - only set if skip is true + "subject": "user-id", + + // The OAuth 2.0 client that initiated the request + "client": {"id": "...", ...}, + + // The initial OAuth 2.0 request url + "request_url": "https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&...", + + // The OAuth 2.0 Scope requested by the client, + "requested_scope": ["foo", "bar"], + + // Information on the OpenID Connect request - only required to process if your UI should support these values. + "oidc_context": {"ui_locales": [...], ...} +} +``` + +For a full documentation on all available keys, please head over to the [API documentation](https://www.ory.sh/docs/api/hydra/) +(make sure to select the right API version). + +Depending of whether or not `skip` is true, you will prompt the user to log in by showing him/her a username/password form, +or by using some other proof of identity. + +If `skip` is true, you **should not** show a user interface but accept the login request directly by making a REST call. +You can use this step to update some internal count of how often a user logged in, or do some other custom business logic. +But again, do not show the user interface. + +To accept the login request, do something along the lines of: + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +const body = { + // This is the user ID of the user that authenticated. If `skip` is true, this must be the `subject` + // value from the `fetch('https://hydra/oauth2/auth/requests/login/' + challenge)` response: + // + // subject = response.subject + // + // Otherwise, this can be a value of your choosing: + subject: "...", + + // If remember is set to true, then the authentication session will be persisted in the user's browser by ORY Hydra. This will set the `skip` flag to true in future requests that are coming from this user. This value has no effect if `skip` was true. + remember: true|false, + + // The time (in seconds) that the cookie should be valid for. Only has an effect if `remember` is true. + remember_for: 3600, + + // This value is specified by OpenID connect and optional - it tells OpenID Connect which level of authentication the user performed - for example 2FA or using some biometric data. The concrete values are up to you here. + acr: ".." +} + +fetch('https://hydra/oauth2/auth/requests/login/' + challenge + '/accept', { + method: 'PUT', + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' } +}). + then(function (response) { + // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next. + res.redirect(response.redirect_to); + }) +``` + +You may also choose to deny the login request. This is possible regardless of the `skip` value. + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +const body = { + error: "...", // This is an error ID like `login_required` or `invalid_request` + error_description: "..." // This is a more detailed description of the error +} + +fetch('https://hydra/oauth2/auth/requests/login/' + challenge + '/reject', { + method: 'PUT', + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' } +}). + then(function (response) { + // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next. + res.redirect(response.redirect_to); + }) +``` + +#### User Consent + +Now that we know who the user is, we must ask the user if he/she wants to grant the requested permissions to the OAuth 2.0 Client. +To do so, we check if the user has previously granted that exact OAuth 2.0 Client the requested permissions. If the user +has never granted any permissions to the client, or the client requires new permissions not previously granted, the user +must visually confirm the request. + +This works very similar to the User Login Flow. +First, the user will be redirect to the Consent Provider which was set using the `OAUTH2_CONSENT_PROVIDER` environment +variable. For example, the user is redirected to `https://consent-provider/consent?consent_challenge=1234` if `OAUTH2_CONSENT_PROVIDER=https://consent-provider/consent`. +This redirection happens *always* and regardless of whether the user has a valid login session or if the user needs +to authorize the application or not. + +The service which handles requests to `https://consent-provider/consent` must first fetch information on the consent +request using a REST API call. Please be aware that for reasons of brevity, the following code snippets are pseudo-code. +For a fully working example, check out our reference [User Login & Consent Provider implementation](https://github.com/ory/hydra-login-consent-node). + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +challenge = req.url.query.consent_challenge; + +fetch('https://hydra/oauth2/auth/requests/consent/' + challenge). + then(function (response) { + // ... + }) +``` + +The server response is a JSON object with the following keys: + +``` +{ + // Skip, if true, let's us know that the client has previously been granted the requested permissions (scope) by the end-user + "skip": true|false, + + // The user-id of the user that will grant (or deny) the request + "subject": "user-id", + + // The OAuth 2.0 client that initiated the request + "client": {"id": "...", ...}, + + // The initial OAuth 2.0 request url + "request_url": "https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&...", + + // The OAuth 2.0 Scope requested by the client, + "requested_scope": ["foo", "bar"], + + // Information on the OpenID Connect request - only required to process if your UI should support these values. + "oidc_context": {"ui_locales": [...], ...} +} +``` + +If skip is true, you should not show any user interface to the user. Instead, you should accept (or deny) the consent request. +Typically, you will accept the request unless you have a very good reason to deny it (e.g. the OAuth 2.0 Client is banned). + +If skip is false and you show the consent screen, you should use the `requested_scope` array to display a list of permissions +which the user must grant (e.g. using a checkbox). Some people choose to always skip this step if the OAuth 2.0 Client +is a first-party client - meaning that the client is used by you or your developers in an internal application. + +Assuming the user accepts the consent request, the code looks very familiar to the User Login Flow. + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +const body = { + // A list of permissions the user granted to the OAuth 2.0 Client. This can be fewer permissions that initially requested, but are rarely more or other permissions than requested. + grant_scope: ["foo", "bar"], + + // If remember is set to true, then the consent response will be remembered for future requests. This will set the `skip` flag to true in future requests that are coming from this user for the granted permissions and that particular client. This value has no effect if `skip` was true. + remember: true|false, + + // The time (in seconds) that the cookie should be valid for. Only has an effect if `remember` is true. + remember_for: 3600, + + // The session allows you to set additional data in the access and ID tokens. + session: { + // Sets session data for the access and refresh token, as well as any future tokens issued by the + // refresh grant. Keep in mind that this data will be available to anyone performing OAuth 2.0 Challenge Introspection. + // If only your services can perform OAuth 2.0 Challenge Introspection, this is usually fine. But if third parties + // can access that endpoint as well, sensitive data from the session might be exposed to them. Use with care! + access_token: { ... }, + + // Sets session data for the OpenID Connect ID token. Keep in mind that the session'id payloads are readable + // by anyone that has access to the ID Challenge. Use with care! + id_token: { ... }, + } +} + +fetch('https://hydra/oauth2/auth/requests/consent/' + challenge + '/accept', { + method: 'PUT', + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' } +}). + then(function (response) { + // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next. + res.redirect(response.redirect_to); + }) +``` + +You may also choose to deny the consent request. This is possible regardless of the `skip` value. + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +const body = { + // This is an error ID like `consent_required` or `invalid_request` + error: "...", + + // This is a more detailed description of the error + error_description: "..." +} + +fetch('https://hydra/oauth2/auth/requests/consent/' + challenge + '/reject', { + method: 'PUT', + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' } +}). + then(function (response) { + // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next. + res.redirect(response.redirect_to); + }) +``` + +Once the user agent is redirected back, the OAuth 2.0 flow will be finalized. + +### Revoking consent and login sessions + +#### Login + +You can revoke login sessions. Revoking a login session will remove all of the user's cookies at ORY Hydra and will require +the user to re-authenticate when performing the next OAuth 2.0 Authorize Code Flow. Be aware that this option will +remove all cookies from all devices. + +Revoking the login sessions of a user is as easy as sending `DELETE to `/oauth2/auth/sessions/login/{user}`. + +#### Consent + +You can revoke a user's consent either on a per application basis or for all applications. Revoking the consent will +automatically revoke all related access and refresh tokens. + +Revoking all consent sessions of a user is as easy as sending `DELETE to `/oauth2/auth/sessions/consent/{user}`. + +Revoking the consent sessions of a user for a specific client is as easy as sending `DELETE to `/oauth2/auth/sessions/consent/{user}/{client}`. + +## OAuth 2.0 Scope + +The scope of an OAuth 2.0 scope defines the permission the token was granted by the user. For example, a specific +token might be allowed to access public pictures, but not private ones. The granted permissions are established during +the consent screen. + +Additionally, ORY Hydra has pre-defined OAuth 2.0 Scope values: + +* `offline` and `offline_access`: Include this scope if you wish to receive a refresh token +* `openid`: Include this scope if you wish to perform an OpenID Connect request. + +## OAuth2 Token Introspection + +OAuth2 Token Introspection is an [IETF](https://tools.ietf.org/html/rfc7662) standard. +It defines a method for a protected resource to query +an OAuth 2.0 authorization server to determine the active state of an +OAuth 2.0 token and to determine meta-information about this token. +OAuth 2.0 deployments can use this method to convey information about +the authorization context of the token from the authorization server +to the protected resource. + +You can find more details on this endpoint in the [ORY Hydra API Docs](https://www.ory.sh/docs/). You can also use +the CLI command `hydra token introspect `. + +## OAuth 2.0 Clients + +You can manage *OAuth 2.0 clients* using the cli or the HTTP REST API. + +* **CLI:** `hydra help clients` +* **REST:** Read the [API Docs](https://www.ory.sh/docs) diff --git a/_legacy/guides/hydra/3-overview/2-jwk.md b/_legacy/guides/hydra/3-overview/2-jwk.md new file mode 100644 index 000000000..3965edb87 --- /dev/null +++ b/_legacy/guides/hydra/3-overview/2-jwk.md @@ -0,0 +1,55 @@ +# JSON Web Keys (JWK) + + + +## Overview + +A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key and is +specified at [IETF RFC7517](https://tools.ietf.org/html/rfc7517). If you've heard of PEM files... + +``` +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDg +MBQGCCqGSIb3DQMHBAgD1kGN4ZslJgSCBMi1xk9jhlPxPc +9g73NQbtqZwI+9X5OhpSg/2ALxlCCjbqvzgSu8gfFZ4yo+ +A .... MANY LINES LIKE THAT .... +X0R+meOaudPTBxoSgCCM51poFgaqt4l6VlTN4FRpj+c/Wc +blK948UAda/bWVmZjXfY4Tztah0CuqlAldOQBzu8TwE7WD +H0ga/iLNvWYexG7FHLRiq5hTj0g9mUPEbeTXuPtOkTEb/0 +GEs= +-----END ENCRYPTED PRIVATE KEY----- +``` + +... JWKs are the same, but formatted using JSON: + +``` +{ + "keys": + [ + {"kty":"oct", + "alg":"A128KW", + "k":"GawgguFyGrWKav7AX4VKUg"}, + + {"kty":"oct", + "k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75 + aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", + "kid":"HMAC key used in JWS spec Appendix A.1 example"} + ] +} +``` + +Hydra offers an API for generating and managing JWKs, the [JSON Web Keys API](http://docs.hydra13.apiary.io/#reference/json-web-keys-jwk). +When using persistent storage backends, the keys are encrypted at rest using AES256-GCM and *the system secret*. +The system secret is generated by default and overridden by the environment variable `SYSTEM_SECRET`. + +JWKs are well supported amongst all languages. This endpoint helps you managing +certificates, private, public and symmetric keys. It is important to never transport keys over insecure channels such as http. + +The [REST API Documentation](https://www.ory.sh/docs/api/hydra/) will give you details on the various endpoints. + +## Auto-generated JWKs + +Hydra generates a couple of JSON Web Keys in order to operate correctly: + +* `http://localhost:4445/keys/hydra.openid.id-token`: A RSA public/private key pair for signing and validating OpenID Connect ID Tokens. +* `http://localhost:4445/keys/https-tls`: A RSA public/private key pair and a certificate for signing HTTP over TLS. diff --git a/_legacy/guides/hydra/3-overview/3-limitations.md b/_legacy/guides/hydra/3-overview/3-limitations.md new file mode 100644 index 000000000..b4e07e3ef --- /dev/null +++ b/_legacy/guides/hydra/3-overview/3-limitations.md @@ -0,0 +1,65 @@ +# Limitations + +ORY Hydra tries to solve all of OAuth 2.0 and OpenID Connect uses. There are, however, some limitations. + + + +## MySQL <= 5.6 / MariaDB + +ORY Hydra has issues with MySQL <= 5.6 (but not MySQL 5.7+) and certain MariaDB versions. Read more about this [here](https://github.com/ory/hydra/issues/377). +Our recommendation is to use MySQL 5.7+ or PostgreSQL. + +## Resource Owner Password Credentials Grant Type (ROCP) + +ORY Hydra does not and will not implement the Resource Owner Password Credentials Grant Type. Read on for context. + +### Overview + +This grant type allows OAuth 2.0 Clients to exchange user credentials (username, password) for an access token. + +**Request:** + +``` +POST /oauth2/token HTTP/1.1 +Host: server.example.com +Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW +Content-Type: application/x-www-form-urlencoded + +grant_type=password&username=johndoe&password=A3ddj3w +``` + +**Response:** + +``` +HTTP/1.1 200 OK +Content-Type: application/json;charset=UTF-8 +Cache-Control: no-store +Pragma: no-cache + +{ + "access_token":"2YotnFZFEjr1zCsicMWpAA", + "token_type":"example", + "expires_in":3600, + "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter":"example_value" +} +``` + +You might think that this is the perfect grant type for your first-party application. This grant type is most commonly +used in mobile authentication for first-party apps. If you plan on doing this, stop right now and read +[this blog article](https://www.ory.sh/oauth2-for-mobile-app-spa-browser). + +### Legacy & Bad Security + +The ROCP grant type is discouraged by developers, professionals, and the IETF itself. It was originally added because +big legacy corporations (not dropping any names, but they are part of the IETF consortium) did not want to migrate their authentication +infrastructure to the modern web but instead do what they've been doing all along "but OAuth 2.0" and for systems that +want to upgrade from OAuth (1.0) to OAuth 2.0. + +There are a ton of good reasons why this is a bad flow, they are summarized in +[this excellent blog article as well](https://www.scottbrady91.com/OAuth/Why-the-Resource-Owner-Password-Credentials-Grant-Type-is-not-Authentication-nor-Suitable-for-Modern-Applications). + +### What about Auth0, Okta, ...? + +Auth0, Okta, Stormpath started early with OAuth 2.0 SaaS and adopted the ROPC grant too. They since deprecated these +old flows but still have them active as existing apps rely on them. diff --git a/_legacy/guides/hydra/3-overview/README.md b/_legacy/guides/hydra/3-overview/README.md new file mode 100644 index 000000000..10e662aa2 --- /dev/null +++ b/_legacy/guides/hydra/3-overview/README.md @@ -0,0 +1,39 @@ +# Concepts and Theory + + + +Please read these chapters carefully, they are imperative to getting started and grasping all the concepts quickly. The +next sections will give you an overview of this chapter and explain some concepts. Do not skip the chapters. Seriously! :) + +The first important concept to understand is that ORY Hydra is an OAuth 2.0 Authorization and OpenID Connect Server. +Some mistake these capabilities for systems that store user data and log you in. This is not the case. Instead, such a +server is responsible for "translating" user credentials (typically username and password) to OAuth 2.0 Access and Refresh Tokens +as well as OpenID Connect ID Tokens. It's basically like you storing cookies with session data, but more flexible and it also +works for third party applications. + +ORY Hydra does not store user profiles, usernames, passwords. This capability is up to you. ORY Hydra uses something we +call a **User Login and Consent Flow**. This flow uses HTTP redirects to forward any incoming authorization request ("Please give +me an access token.") to the **Login Provider** and the **Consent Provider**. These applications are something you implement. +It can be a new app or your existing login system. On a high level, these providers can be summarized as: + +- The login provider is responsible for authenticating the user ("login") by validating his or her credentials (e.g. username + password). +- The consent provider is responsible for allowing the OAuth 2.0 application to get a token on the user's behalf ("Do you want +to allow foobar-app access to all your personal messages and images?". + +A second important concept is the OAuth 2.0 Scope. + +Often, developers confuse OAuth 2.0 Scope with regular Access Control. OAuth 2.0 Scope and, for example, Role Based Access +Control (RBAC) or Access Control Lists (ACL) cover different aspects of access control. + +Your internal access control will tell you what a user can do in your system. An administrator might modify everything, +a regular user might only be allowed to read personal messages. The OAuth 2.0 Scope does not represent what a resource owner ("user") is able to +do in a system or not. They do not express things like administrative rights. + +The OAuth 2.0 Scope expresses what a user allowed an OAuth 2.0 Client (read: "access token") to do on his/her behalf. +For example, an access token might be allowed to see a user's pictures, but not upload new pictures on his/her behalf. +The user him/herself however is generally allowed to view and upload pictures. **The OAuth 2.0 Scope do not express a user's permissions.** +They express what an OAuth 2.0 Client may do on the user's behalf - independently of whether or not the user is actually allowed +to do that. For example, the user could lie and say that the client is allowed to access some protected resource which +he does not have access to ("Read all classified documents", but he is not allowed to view any classified documents). + +This concludes the overview of the two most important pieces of ORY Hydra. diff --git a/guides/hydra/4-install/README.md b/_legacy/guides/hydra/4-install/README.md similarity index 100% rename from guides/hydra/4-install/README.md rename to _legacy/guides/hydra/4-install/README.md diff --git a/_legacy/guides/hydra/5-security/README.md b/_legacy/guides/hydra/5-security/README.md new file mode 100644 index 000000000..e40e6198b --- /dev/null +++ b/_legacy/guides/hydra/5-security/README.md @@ -0,0 +1,102 @@ +# Security Overview + +Hydra is built with tough security in mind. + + + +## OAuth 2.0 Security Overview + +Hydra is an implementation of the security-first Fosite OAuth 2.0 SDK +([https://github.com/ory/fosite](https://github.com/ory/fosite)). Fosite respects +the [OAuth 2.0 Threat Model and Security Considerations](https://tools.ietf.org/html/rfc6819#section-5.1.5.3) by +the IETF, specifically: + +- No Cleartext Storage of Credentials +- Encryption of Credentials +- Use Short Expiration Time +- Limit Number of Usages or One-Time Usage +- Bind Token to Client id +- Automatic Revocation of Derived Tokens If Abuse Is Detected +- Binding of Refresh Token to "client_id" +- Refresh Token Rotation +- Revocation of Refresh Tokens +- Validate Pre-Registered "redirect_uri" +- Binding of Authorization "code" to "client_id" +- Binding of Authorization "code" to "redirect_uri" +- Opaque access tokens +- Opaque refresh tokens +- Ensure Confidentiality of Requests +- Use of Asymmetric Cryptography +- Enforcing random states: Without a random-looking state or OpenID Connect nonce the request will fail. + +Additionally these safeguards are implemented: + +- Advanced Token Validation: Tokens are laid out as <key>.<signature> where <signature> +is created using HMAC-SHA256 using a global secret. + +### Advanced Token Validation (Datastore Security) + +For a OAuth2 access token, refresh token or authorize code to be valid, one requires both the key and +the signature (formatted as <key>.<signature>). Only the signature is stored in the datastore (SQL), +thus a compromised datastore will not allow an attacker to gain access to any valid authorize codes, +access tokens, or refresh tokens. + +Because HMAC-SHA256 is used, the System Secret is required to create valid key-signature pairs, rendering an attacker +unable to inject new codes or tokens into a compromised datastore. + +## Cryptography + +Hydra uses different cryptographic methods, this is an overview of all of them. + +### AES-GCM + +AES-GCM is used to encrypt JWKs at rest using a key size of 256 bit which exceeds requirements by Lenstra, +ECRYPT II, NIST, ANSSI, and BSI, see [https://www.keylength.com/en/compare/](https://www.keylength.com/en/compare/). + +GCM (Galois/Counter Mode) is an authenticated encryption algorithm designed to provide both data authenticity +(integrity) and confidentiality. GCM uses a nonce (“IV”) that has an upper limit of 2^32 nonces. If more +nonces are used, there is risk of repeats. This means that you risk collisions when storing more than 2^32 +documents authenticated with GCM. Because AES-GCM is only used to encrypt data at rest, this is might +only impose a problem if + +1. more than 2^32 documents are stored using AES-GCM +2. an attacker gains access to the datastore where > 2^32 documents are stored +3. the attacker is able to exploit repeats, for example by authenticating malicious documents + +### RS256 + +RSASSA-PKCS1-v1_5 using SHA-256 (RS256) is used to sign JWTs. It’s use is recommended by the JWA +specification, see [https://www.rfc-editor.org/rfc/rfc7518.txt](https://www.rfc-editor.org/rfc/rfc7518.txt) + +The RSA Key size is 4096 bit long, exceeding the minimum requirement of 2048 bit by +[https://www.rfc-editor.org/rfc/rfc7518.txt](https://www.rfc-editor.org/rfc/rfc7518.txt). + +Recommendations from NIST, ANSSI, IAD-NSA, BSI, Lenstra and others vary between 1300 and 2048 bit key +lengths for asymmetric cryptography based on discrete logarithms (RSA). 4096 exceeds all recommendations +for 2017 from all authorities, see [https://www.keylength.com/en/compare/](https://www.keylength.com/en/compare/). + +### HMAC-SHA256 + +HMAC (FIPS 198) with SHA256 (FIPS 180-4) is used to sign access tokens, authorize codes and refresh +tokens. SHA-2 (with 256 bit) is encouraged by NIST, see +[http://csrc.nist.gov/groups/ST/hash/policy.html](http://csrc.nist.gov/groups/ST/hash/policy.html) + +### BCrypt + +BCrypt is used to hash client credentials at rest. It is not officially recommended by NIST as +it is not based on hashing primitives such as SHA-2, but rather on Blowfish. However, BCrypt is +much stronger than any other (salted) hashing method for passwords, has wide adoption +and is an official golang/x library. + +I recommend reading this thread on Security Stack Exchange on BCrypt, SCrypt +and PBKDF2: [https://security.stackexchange.com/questions/4781/do-any-security-experts-recommend-bcrypt-for-password-storage](https://security.stackexchange.com/questions/4781/do-any-security-experts-recommend-bcrypt-for-password-storage) + +Be aware that BCrypt causes very high CPU loads, depending on the Workload Factor. We +strongly advise reducing the number of requests that use Basic Authorization. + +## How does Access Control work with Hydra? + +Hydra supports two concepts of authorization. One is called Token Introspection which is a standard +by the IETF ( [https://tools.ietf.org/html/rfc7662](https://tools.ietf.org/html/rfc7662) ). It is primarily +targeted at third-party services and is usually used by a programmatic API. It can be used by first-party +services too. The sole purpose of this endpoint is to check whether an access token is valid or not. diff --git a/_legacy/guides/hydra/6-how-to/1-production.md b/_legacy/guides/hydra/6-how-to/1-production.md new file mode 100644 index 000000000..779ee440b --- /dev/null +++ b/_legacy/guides/hydra/6-how-to/1-production.md @@ -0,0 +1,72 @@ +# ORY Hydra in Production + +This page aims to help you setting up a production system with ORY Hydra. + + + +## Configuration + +All configuration of ORY Hydra is currently done via environment variables. Setting environment variables works +differently on each system, so we collected some to help you get started. + +### Linux / OSX + +``` +$ export MY_ENV_VAR=foo +$ hydra ... +# or +$ MY_ENV_VAR=foo hydra ... +``` + +### Windows + +#### Command Prompt + +``` +$ set MY_ENV_VAR=foo +$ hydra ... +``` + +#### Powershell + +``` +$ $env:MY_ENV_VAR="foo" +$ hydra ... +``` + +### Docker + +``` +$ docker run -e MY_ENV_VAR=foo oryd/hydra:... +``` + +## ORY Hydra behind an API Gateway + +Although ORY Hydra implements all Go best practices around running public-facing production http servers, we discourage running +ORY Hydra facing the public net directly. We strongly recommend running ORY Hydra behind an API gateway or a load balancer. +It is common to terminate TLS on the edge (gateway / load balancer) and use certificates provided by your infrastructure +provider (e.g. AWS CA) for last mile security. + +### TLS Termination + +You may also choose to set Hydra to HTTPS mode without actually accepting TLS connections. In that case, +all Hydra URLs are prefixed with `https://`, but the server is actually accepting http. This makes sense if you don't want +last mile security using TLS, and trust your network to properly handle internal traffic. To use this setting, check +for `HTTPS_ALLOW_TERMINATION_FROM` in `hydra help host`. + +With TLS termination enabled, ORY Hydra discards all requests unless: + +* The request is coming from a trusted IP address set by `HTTPS_ALLOW_TERMINATION_FROM` and the header `X-Forwarded-Proto` is set to `https`. +* The request goes to `/health/status` which does not require TLS termination and that is used to check the health of an instance. + +If you are unable to properly set up TLS Termination, you may want to set the `--dangerous-force-http` flag. But please be +aware that we discourage you from doing so and that you should know what you're doing. + +### Routing + +It is common to use a router, or API gateway, to route subdomains or paths to a specific service. For example, `https://myservice.com/hydra/` +is routed to `http://10.0.1.213:3912/` where `10.0.1.213` is the host running ORY Hydra. To compute the values for +the consent challenge, ORY Hydra uses the host and path headers from the HTTP request. Therefore, it is important +to set up your API Gateway in such a way, that it passes the public host (in this case `myservice.com`) and the path +without any prefix (in this case `hydra/`). If you use the Mashape Kong API gateway, you can achieve this by setting +`strip_request_path=true` and `preserve_host=true.` diff --git a/_legacy/guides/hydra/6-how-to/2-architecture.md b/_legacy/guides/hydra/6-how-to/2-architecture.md new file mode 100644 index 000000000..152485fd7 --- /dev/null +++ b/_legacy/guides/hydra/6-how-to/2-architecture.md @@ -0,0 +1,76 @@ +# Integrating ORY Hydra + +This article explains how you to integrate ORY Hydra in your system. + + + +## Overview + +A high-level overview of the interaction between a client, ORY Hydra (Authorization Server) and an API looks as follows: + +[`sequenceDiagram + participant Client + participant ORY Hydra + participant API + + Client->>ORY Hydra: Perform OAuth 2.0 Flow + ORY Hydra->>Client: Access Token + Client->>API: Request with Access Token + API->ORY Hydra: Validates Access Token + API->>Client: Response`](./images/basic-oauth2-system.png) + +Most of what is explained here can also be seen as real-life examples in the [ory/examples](https://github.com/ory/examples) +repository! + +### Interacting with OAuth 2.0 + +**Please, do not write your own code to interact with OAuth 2.0**. Use open source & battle-tested libraries instead. Here are some +examples: + +* NodeJS + * [passport](http://www.passportjs.org/) + * [simple-oauth2](https://github.com/lelylan/simple-oauth2) +* Golang + * [golang/oauth2](https://github.com/golang/oauth2) **recommended* +* PHP + * [oauth2-client](https://github.com/thephpleague/oauth2-client) +* Java + * [Sprint Security OAuth](https://spring.io/projects/spring-security-oauth) + +For a full list of client libraries go [here](https://oauth.net/code/). + +### Validating OAuth 2.0 Access Tokens + +The best and easiest way to validate OAuth 2.0 Access Tokens is by performing OAuth 2.0 Token Introspection. You can +do this with the CLI `hydra token introspect `. + +#### NodeJS + +``` +const token = 'the access token' +const body = qs.stringify({ token }) + +fetch('http://ory-hydra/oauth2/introspect', { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': body.length + }, + method: 'POST', body +}).then(body => { + if (!body.active) { + // Token is not active/valid + } else if (body.token_type !== 'access_token') { + // Token is not an access token (probably a refresh token) + } + + // token is active +}) +``` + +#### CURL + +```bash +$ curl -X POST \ + -d 'token=' \ + http://localhost:4445/oauth2/introspect +``` diff --git a/guides/hydra/6-how-to/3-advanced.md b/_legacy/guides/hydra/6-how-to/3-advanced.md similarity index 100% rename from guides/hydra/6-how-to/3-advanced.md rename to _legacy/guides/hydra/6-how-to/3-advanced.md diff --git a/_legacy/guides/hydra/6-how-to/4-debug.md b/_legacy/guides/hydra/6-how-to/4-debug.md new file mode 100644 index 000000000..cfe925845 --- /dev/null +++ b/_legacy/guides/hydra/6-how-to/4-debug.md @@ -0,0 +1,199 @@ +# Debugging + +Spec-compliant OAuth 2.0 and OpenID Connect is hard. Let's take a look how to resolve certain issues. + + + +## First Aid + +There are three things you can do to quickly debug any issue: + +1. Check the logs. ORY Hydra has extensive logging and you will find the issue most likely in the logs. Here is an example +log line for a client that requested a redirect URL that did not match the whitelisted redirect URLS: `time="2018-08-07T16:01:16Z" level=error msg="An error occurred" description="The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed" error=invalid_request hint="The \"redirect_uri\" parameter does not match any of the OAuth 2.0 Client's pre-registered redirect urls."` +2. Check the URL because of two reasons: + 1. ORY Hydra sends `error_hint`, `error`, `error_description`, `error_debug` in the URL. You will find the + cause of the error most likely in there. + 2. You are maybe in the wrong URL. Make sure the host and port and path is correct. This happens often, especially + when you're just starting out and experimenting +3. Set environment variable `OAUTH2_SHARE_ERROR_DEBUG=true`. Do not do this in production, it is possible, though unlikely, +that important data leaks with this. If set to true, ORY Hydra will set the `error_debug` query parameter if debug +information is available. +4. If you're just starting out and experimenting your docker set up does not work at all: + 1. Stop all containers + 2. Remove them (unless you have something important running) + 3. Retry. **This can help a lot if you are new to this!** + +## OAuth 2.0 Authorize Code Flow fails + +The most likely cause is misconfiguration, summarized in the next sections. + +### Wrong or misconfigured OAuth 2.0 Client + +You are using the wrong OAuth 2.0 Client or the OAuth 2.0 Client has a broken configuration. To check that +you're using the right client, run: + +``` +hydra clients get --endpoint http://ory-hydra +``` + +The result shows you the whole client (excluding its secret). Check that the values are correct. Example: + +``` +{ + "client_id": "my-client", + "grant_types": [ + "authorization_code" + ], + "jwks": {}, + "redirect_uris": [ + "http://127.0.0.1:5556/callback" + ], + "response_types": [ + "code" + ], + "scope": "openid offline", + "subject_type": "pairwise", + "token_endpoint_auth_method": "client_secret_basic", + "userinfo_signed_response_alg": "none" +} +``` + +### Redirect URL is not whitelisted + +A likely cause of your request failing is that you are using the wrong redirect URL. Assuming your OAuth 2.0 +URL looks like `http://ory-hydra/oauth2/auth?client_id=my-client&...&redirect_uri=http://my-url/callback` + +The redirect URL `http://my-url/callback` must be whitelisted in your client configuration. The URLs must match **exactly**. +URL `http://my-url/callback` and `http://my-url/callback?foo=bar` are different URLs! + +To see the whitelisted redirect_uris, check the client: + +``` +hydra clients get --endpoint http://ory-hydra + +{ + // ... + "redirect_uris": [ + "http://127.0.0.1:5556/callback" + ], + // ... +} +``` + +Here you see that `http://my-url/callback` is not in the list, which is why the request fails. + +### OAuth 2.0 Client ID and secret are sent in body instead of header + +There are multiple ways of authenticating OAuth 2.0 Clients at the `/oauth2/token`: + +* HTTP Basic Authorization (`client_secret_basic`) - the OAuth 2.0 Client ID and secret are sent in the HTTP Header (`Authorization: basic ....`) +* HTTP Body (`client_secret_post`) - the OAuth 2.0 Client ID and secret are sent in the POST body (`Content-Type: application/x-www-form-urlencoded`) + +Both are valid schemes. But the OAuth 2.0 Client has to be configured to allow either of the one. Per default, the OAuth 2.0 +Client allows HTTP Basic Authorization only. You can check which method is allowed: + +``` +hydra clients get --endpoint http://ory-hydra +{ + // ... + "token_endpoint_auth_method": "client_secret_basic", + // ... +} +``` + +As you can see, this client is allowed to authorize using HTTP Basic Authorization. If you try to authorize with the client +credentials in the POST body, the authentication process will fail. To allow a client to perform the POST authorization +scheme, you must set `"token_endpoint_auth_method": "client_secret_post"`. You can do this in the CLI with the +`--token-endpoint-auth-method` flag. + +## Distributed Tracing + +### What is this? + +Configuring Distributed Tracing (DT) will enable you to obtain a visualization of the call paths that take place in order to process a request made to Hydra. It's yet another tool that you can use to aid you in profiling, debugging and ultimately understanding your deployment of Hydra better. Hydra currently supports the following tracing options: + +- Tracing backend(s): Jaeger - _Note: adding support for other [opentracing compliant backends](https://opentracing.io/docs/supported-tracers) is planned. To aid in priority, please [create an issue](https://github.com/ory/hydra/issues) with your feature request._ +- Following existing traces: If you have deployed Hydra behind a proxy that has initiated a trace, Hydra will attempt to join that trace by examining the request headers for tracing context. + +### What a Hydra trace includes + +In DT speak, a trace is comprised of one or more spans which are logical units of work. Each Hydra span is encapsulated with the following state: + +- A name +- A start time +- A finish time +- A set of zero or more tags + +Hydra currently creates the following spans: + +- Top level span (_named after the request path_) for the requested endpoint. Span tags: + - http method + - http status code + - error IFF status code >= 400 +- Child span will be created if bcrypt (_e.g. when the token endpoint is called_) is called. Span tags: + - bcrypt work factor +- All SQL database interactions. Spans/tags will vary depending on the database driver used. + +This is still evolving and subject to change as tracing support continues to expand in Hydra. If you see something that is missing/wrong, please create an issue. + +### Alright, how can I set this up locally? + +The [provided docker-compose file](https://github.com/ory/hydra/blob/master/docker-compose.yml) in the project repository has tracing configuration w/ jaeger added which you can use to play around with. Simply uncomment the configurations associated with tracing as so: + +**Under the Hydra service definition `depends_on` configs, uncomment the following:** + +``` +- jaeger +``` + +**Under the Hydra service definition `environment` configs, uncomment the following:** + +``` +- TRACING_PROVIDER +- TRACING_PROVIDER_JAEGER_SAMPLING_SERVER_URL +- TRACING_PROVIDER_JAEGER_LOCAL_AGENT_ADDRESS +- TRACING_PROVIDER_JAEGER_SAMPLING_TYPE +- TRACING_PROVIDER_JAEGER_SAMPLING_VALUE +``` + +**Uncomment the Jaeger service definition:** + +``` +jaeger: + image: jaegertracing/all-in-one:1.7.0 + ports: + - "5775:5775/udp" + - "6831:6831/udp" + - "6832:6832/udp" + - "5778:5778" + - "16686:16686" + - "14268:14268" + - "9411:9411" +``` + +Then simply run `docker-compose up`. Grab a coffee or stretch while you wait for everything to come up. You will then be able to navigate to the Jaeger UI +which you have exposed on port `16686` at http://localhost:16686/search. You can now start making requests to Hydra and inspect traces! + +As an example, here is a trace created by making a bad request to the `POST /clients` endpoint: + +![Sample Trace](../images/sample_trace.png) + +At a glance, you are able to see that: + +- The request failed +- The request took ~80ms +- It resulted in a 409 +- The hash comparison to validate the client's credentials took a whopping 70ms. Bcrypt is expensive! +- The various database operations performed + +*Note: in order to see spans around database interactions, you must be using a SQL backend (i.e. MySQL or Postgres).* + +### Tracing configurations + +The CLI will provide you with the list of Hydra tracing configurations and their supported values. Simply run: + +``` +docker exec -it hydra_hydra_1 hydra serve --help +``` + +And read the section on `DEBUG CONTROLS`. \ No newline at end of file diff --git a/_legacy/guides/hydra/6-how-to/README.md b/_legacy/guides/hydra/6-how-to/README.md new file mode 100644 index 000000000..84344d10f --- /dev/null +++ b/_legacy/guides/hydra/6-how-to/README.md @@ -0,0 +1,3 @@ +# How To + + \ No newline at end of file diff --git a/_legacy/guides/hydra/7-sdk/1-go.md b/_legacy/guides/hydra/7-sdk/1-go.md new file mode 100644 index 000000000..bb93af05c --- /dev/null +++ b/_legacy/guides/hydra/7-sdk/1-go.md @@ -0,0 +1,71 @@ +## Go SDK + + + +### Installation + +To install the Go SDK, run: + +``` +go get -u -d github.com/ory/hydra/sdk/go/hydra +``` + +### Configuration + +The Go SDK is auto generated from swagger but contains some helpers, such as `NewSDK`: + +```go +import "github.com/ory/hydra/sdk/go/hydra" + +sdk, err := hydra.NewSDK(&hydra.Configuration{ + AdminURL: "https://hydra.localhost:4445", +}) +``` + +#### With OAuth 2.0 + +If you want to send an OAuth 2.0 Access Token while accessing ORY Hydra's Admin API (e.g. because you protect it using +ORY Oathkeeper), you can configure the SDK to do that: + +```go +import "github.com/ory/hydra/sdk/go/hydra" + +sdk, err := hydra.NewSDK(&hydra.Configuration{ + AdminURL: "https://hydra.localhost:4445", + PublicURL: "https://hydra.localhost:4444", + ClientID: "...", + ClientSecret: "...", + Scopes: []string{"..."}, +}) +``` + +### API Usage + +APIs usually have three return values. Please check for errors as well as status codes! + +```go +client, response, error := sdk.CreateClient(swagger.Client{ /* .... payload .... */}) +if err != nil { + // This usually indicates a network error. +} else if response.StatusCode != http.StatusCreated { + // If the status code is not 2xx, something went wrong on the application level (e.g. wrong credentials, database offline, ...) +} + +fmt.Printf("Client created: %+v", client) +``` + +In rare cases, methods have only two return values. This happens when the REST API returns `204 No Content`: + +``` +response, err := sdk.DeleteClient("client-id") +if err != nil { + // This usually indicates a network error. +} else if response.StatusCode != http.StatusNoContent { + // If the status code is not 2xx, something went wrong on the application level (e.g. wrong credentials, database offline, ...) +} +``` + +### API Docs + +API docs are available [here](https://github.com/ory/hydra/blob/master/sdk/go/hydra/swagger/README.md). +Please note that those docs are generated and may introduce bugs if code examples are used 1:1. diff --git a/_legacy/guides/hydra/7-sdk/2-js.md b/_legacy/guides/hydra/7-sdk/2-js.md new file mode 100644 index 000000000..60cc85100 --- /dev/null +++ b/_legacy/guides/hydra/7-sdk/2-js.md @@ -0,0 +1,51 @@ +## JavaScript SDK + + + +### Installation + +To install the JavaScript SDK, run: + +``` +npm install --save ory-hydra-sdk +``` + +### Configuration + +#### Basic configuration + +```js +const Hydra = require('ory-hydra-sdk') + +// Set this to Hydra's URL +Hydra.ApiClient.instance.basePath = 'http://localhost:4445' + +``` + +### API Usage + +Let's use `refreshToken` to request a new access token and make +an authorized API call: + +```js +const hydra = new Hydra.OAuth2Api() + +// for example, let's fetch all OAuth2 clients +hydra.listOAuth2Clients((error, data, response) => { + if (error) { + // a network error occurred. + throw error + } else if (response.statusCode < 200 || response.statusCode >= 400) { + // an application error occurred. + throw new Error('Consent endpoint gave status code ' + response.statusCode + ', but status code 200 was expected.') + } + + console.log(response) // a list of OAuth2 clients. +}) +``` + +### API Docs + +API docs are available [here](https://github.com/ory/hydra/blob/master/sdk/js/hydra/swagger/README.md). +Please note that those docs are generated and may introduce bugs if code examples are used 1:1. Especially +the package name is not correct. diff --git a/_legacy/guides/hydra/7-sdk/3-php.md b/_legacy/guides/hydra/7-sdk/3-php.md new file mode 100644 index 000000000..ab3b0dfdc --- /dev/null +++ b/_legacy/guides/hydra/7-sdk/3-php.md @@ -0,0 +1,86 @@ +## PHP SDK + + + +**Please note that this section is not updated to ORY Hydra 1.0.0 but reflects the old API. If you know PHP, please +provide documentation on how to get started with the PHP SDK.** + +### Installation + +Installation is best done using [composer](https://getcomposer.org/) + +``` +composer require ory/hydra-sdk +``` + +If your project doesn't already make use of composer, you will need to include the resulting `vendor/autoload.php` file. + +### Configuration + +#### OAuth2 configuration + +We need OAuth2 capabilities in order to make authorized API calls. You can either write your own OAuth2 mechanism or +use an existing one that has been preconfigured for use with Hydra. Here we use a modified version of the league OAuth2 +client that has had this work done for us. + +```sh +composer require ory/oauth2-hydra +``` + +```php +// Get an access token using your account credentials. +// Note that if you are using the Hydra inside docker as per the getting started docs, the domain will be hydra:4444 from +// within another container. +$provider = new \Hydra\OAuth2\Provider\OAuth2([ + 'clientId' => 'admin', + 'clientSecret' => 'demo-password', + 'domain' => 'http://localhost:4445', +]); + +try { + // Get an access token using the client credentials grant. + // Note that you must separate multiple scopes with a plus (+) + $accessToken = $provider->getAccessToken( + 'client_credentials', ['scope' => 'hydra.clients'] + ); +} catch (\Hydra\Oauth2\Provider\Exception\ConnectionException $e) { + die("Connection to hydra failed: " . $e->getMessage()); +} catch (\Hydra\Oauth2\Provider\Exception\IdentityProviderException $e) { + die("Failed to get an access token: " . $e->getMessage()); +} + +``` + +#### SDK configuration + +Using `$accessToken` from the above steps, you may now use the Hydra SDK: + +```php +$config = new \Hydra\SDK\Configuration(); +$config->setHost('http://localhost:4445'); +// Use true in production! +$config->setSSLVerification(false); +$config->setAccessToken($accessToken); + +// Pass the config into an ApiClient. You will need this client in the next ste. +$hydraApiClient = new \Hydra\SDK\ApiClient($config); +``` + +### API Usage + +There are several APIs made available, see [the full API docs](https://github.com/ory/hydra/blob/master/sdk/php/swagger/README.md) for a list of clients and methods. + +For this example, lets use the OAuth2Api to get a list of clients and use the `$hydraApiClient` from above: + +```php +$hydraOAuth2Api = new \Hydra\SDK\Api\OAuth2Api($hydraApiClient); + +try { + $clients = $hydraOAuthSDK->listOAuth2Clients(); +} catch ( \Hydra\SDK\ApiException $e) { + if ($e->getCode() == 400) { + die("Permission denied to get clients. Check the scopes on your access token!"); + } + die("Failed to get clients: ".$e->getMessage()); +} +``` diff --git a/_legacy/guides/hydra/7-sdk/README.md b/_legacy/guides/hydra/7-sdk/README.md new file mode 100644 index 000000000..a7b9d4851 --- /dev/null +++ b/_legacy/guides/hydra/7-sdk/README.md @@ -0,0 +1,32 @@ +# SDK + +All SDKs are use automated code generation provided by [`swagger-codegen`](https://github.com/swagger-api/swagger-codegen). +Unfortunately, `swagger-codegen` has serious breaking changes in the generated code when upgrading versions. Therefore, +we do not make backwards compatibility promises with regards to the generated SDKs. We hope to improve this process +in the future. + +We encourage you to not use the SDKs (due to mediocre code quality) but to implement the simple REST +calls yourself instead. Most calls can be handled with one-liners in many languages. In JavaScript you can create +a client with: + +``` +fetch( + 'http://hydra/clients', + { + method: 'POST', + headers: {"Content-Type": "application/json"}, + body: JSON.encode({ client_id: 'foo', client_secret: 'bar' }) + } +).then(r => r.JSON())` + +**The SDKs do not provide a good API for dealing with OAuth 2.0 Flows (e.g. Authorize Code Flow, Refresh Flow, ...). +But there are tons of [libraries available for consuming OAuth 2.0](https://oauth.net/code/). Do not write your +own OAuth 2.0 Library!** + +If you want to use the SDK despite its caveats (mediocre code quality, potential breaking changes), you will find more information here: + +* [Golang](./1-go.md) +* [JavaScript](./2-js.md) +* [PHP](./3-php.md) + +Missing your programming language? [Create an issue](https://github.com/ory/hydra/issues) and help us build, test and publish the SDK for your programming language! diff --git a/guides/hydra/8-faq/README.md b/_legacy/guides/hydra/8-faq/README.md similarity index 100% rename from guides/hydra/8-faq/README.md rename to _legacy/guides/hydra/8-faq/README.md diff --git a/_legacy/guides/hydra/9-appendix/README.md b/_legacy/guides/hydra/9-appendix/README.md new file mode 100644 index 000000000..f48bdbb0d --- /dev/null +++ b/_legacy/guides/hydra/9-appendix/README.md @@ -0,0 +1,211 @@ +# Appendix + +## `serve` configuration and flags + +``` +$ hydra help serve + +ORY Hydra exposes two ports, a public and an administrative port. The public port is responsible +for handling requests from the public internet, such as the OAuth 2.0 Authorize and Token URLs. The administrative +port handles administrative requests like creating OAuth 2.0 Clients, managing JSON Web Keys, and managing User Login +and Consent sessions. + +It is recommended to run "hydra serve all". If you need granular control over CORS settings or similar, you may +want to run "hydra serve admin" and "admin serve public" separately. + +To learn more about each individual command, run: + +- hydra help serve all +- hydra help serve admin +- hydra help serve public + +All sub-commands share command line flags and the following environment variable names: + +CORE CONTROLS +============= + +- DATABASE_URL: A URL to a persistent backend. Hydra supports various backends: + - Memory: If DATABASE_URL is "memory", data will be written to memory and is lost when you restart this instance. + Example: DATABASE_URL=memory + + - Postgres: If DATABASE_URL is a DSN starting with postgres:// PostgreSQL will be used as storage backend. + Example: DATABASE_URL=postgres://user:password@host:123/database + + If PostgreSQL is not serving TLS, append ?sslmode=disable to the url: + DATABASE_URL=postgres://user:password@host:123/database?sslmode=disable + + - MySQL: If DATABASE_URL is a DSN starting with mysql:// MySQL will be used as storage backend. + Example: DATABASE_URL=mysql://user:password@tcp(host:123)/database?parseTime=true + + Be aware that the ?parseTime=true parameter is mandatory, or timestamps will not work. + +- SYSTEM_SECRET: A secret that is at least 16 characters long. If none is provided, one will be generated. They key + is used to encrypt sensitive data using AES-GCM (256 bit) and validate HMAC signatures. + Example: SYSTEM_SECRET=jf89-jgklAS9gk3rkAF90dfsk + +- COOKIE_SECRET: A secret that is used to encrypt cookie sessions. Defaults to SYSTEM_SECRET. It is recommended to use + a separate secret in production. + Example: COOKIE_SECRET=fjah8uFhgjSiuf-AS + +- PORT: The port hydra should listen on. + Defaults to PORT=4444 + +- HOST: The host interface hydra should listen on. Leave empty to listen on all interfaces. + Example: HOST=localhost + +- BCRYPT_COST: Set the bcrypt hashing cost. This is a trade off between + security and performance. Range is 4 =< x =< 31. + Defaults to BCRYPT_COST=10 + +- LOG_LEVEL: Set the log level, supports "panic", "fatal", "error", "warn", "info" and "debug". Defaults to "info". + Example: LOG_LEVEL=panic + +- LOG_FORMAT: Leave empty for text based log format, or set to "json" for JSON formatting. + Example: LOG_FORMAT="json" + +- DISABLE_TELEMETRY: Set to "1" to disable telemetry collection and sharing - for more information please + visit https://ory.gitbooks.io/hydra/content/telemetry.html + Example: DISABLE_TELEMETRY="1" + +- RESOURCE_NAME_PREFIX: Allows the alternation of the "rn:hydra:" prefix in all resource names declared by ORY Hydra. + Defaults to "rn:hydra" if empty and removes the last trailing colon. + Example: RESOURCE_NAME_PREFIX="resources:my-domain.com" + + +OAUTH2 CONTROLS +=============== + +- OAUTH2_ERROR_URL: A dedicated endpoint that shows critical errors in a user-friendly way. + Example: OAUTH2_ERROR_URL=https://id.myapp.com/error + +- OAUTH2_CONSENT_URL: The consent provider's URL. + Example: OAUTH2_CONSENT_URL=https://id.myapp.com/consent + +- OAUTH2_LOGIN_URL: The login provider's URL. + Example: OAUTH2_LOGIN_URL=https://id.myapp.com/login + +- OAUTH2_ISSUER_URL: IssuerURL is the public URL of your Hydra installation. It is used for OAuth2 and OpenID Connect and must be + specified and using HTTPS protocol, unless --dangerous-force-http is set. + Example: OAUTH2_ISSUER_URL=https://hydra.myapp.com/ + +- AUTH_CODE_LIFESPAN: Lifespan of OAuth2 authorize codes. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + Defaults to AUTH_CODE_LIFESPAN=10m + +- ID_TOKEN_LIFESPAN: Lifespan of OpenID Connect ID Tokens. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + Defaults to ID_TOKEN_LIFESPAN=1h + +- ACCESS_TOKEN_LIFESPAN: Lifespan of OAuth2 access tokens. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + Defaults to ACCESS_TOKEN_LIFESPAN=1h + +- CHALLENGE_TOKEN_LIFESPAN: Lifespan of OAuth2 consent tokens. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + Defaults to CHALLENGE_TOKEN_LIFESPAN=10m + +- SCOPE_STRATEGY: Set this to DEPRECATED_HIERARCHICAL_SCOPE_STRATEGY to enable the deprecated hierarchical scope strategy. + This is required if you do not want to migrate to the new wildcard strategy. + +- OAUTH2_SHARE_ERROR_DEBUG: Set this to true if you want to share error debugging information with your OAuth 2.0 clients. + Keep in mind that debug information is very valuable when dealing with errors, but might also expose database error + codes and similar errors. + Defaults to OAUTH2_SHARE_ERROR_DEBUG=false + +- OAUTH2_ACCESS_TOKEN_STRATEGY: Sets the Access Token Strategy. Defaults to "opaque" which is the recommended strategy + for usage with ORY Hydra. If set to "jwt", then Access Tokens will be a signed JSON Web Token. The public key + for verifying the token can be obtained from "./well-known/jwks.json". Please note that the "jwt" strategy is currently + in BETA and not recommended for production just yet. + Defaults to OAUTH2_ACCESS_TOKEN_STRATEGY="opaque" + + +OPENID CONNECT CONTROLS +=============== + +- OIDC_DISCOVERY_CLAIMS_SUPPORTED: A comma separated list of supported claims to be advertised at the OpenID Connect + Discovery endpoint /.well-known/openid-configuration. Always adds "sub" to the supported claims. + +- OIDC_DISCOVERY_SCOPES_SUPPORTED: A comma separated list of supported scopes to be advertised at the OpenID Connect + Discovery endpoint /.well-known/openid-configuration. Always adds "offline", "openid" to the supported scopes. + +- OIDC_DISCOVERY_USERINFO_ENDPOINT: A URL of the userinfo endpoint to be advertised at the OpenID Connect + Discovery endpoint /.well-known/openid-configuration. Defaults to ORY Hydra's userinfo endpoint at /userinfo. + Set this value if you want to handle this endpoint yourself. + +- OIDC_DYNAMIC_CLIENT_REGISTRATION_DEFAULT_SCOPE: The OpenID Connect Dynamic Client Registration specification + has no concept of whitelisting OAuth 2.0 Scope. If you want to expose Dynamic Client Registration, you should set the default + scope enabled for newly registered clients. Keep in mind that users can overwrite this default by setting the + "scope" key in the registration payload, effectively disabling the concept of whitelisted scopes. + Example: OIDC_DYNAMIC_CLIENT_REGISTRATION_DEFAULT_SCOPE=openid,offline,scope-a,scope-b + + +HTTPS CONTROLS +============== + +- HTTPS_ALLOW_TERMINATION_FROM: Whitelist one or multiple CIDR address ranges and allow them to terminate TLS connections. + Be aware that the X-Forwarded-Proto header must be set and must never be modifiable by anyone but + your proxy / gateway / load balancer. Supports ipv4 and ipv6. + Hydra serves http instead of https when this option is set. + Example: HTTPS_ALLOW_TERMINATION_FROM=127.0.0.1/32,192.168.178.0/24,2620:0:2d0:200::7/32 + +- HTTPS_TLS_CERT_PATH: The path to the TLS certificate (pem encoded). + Example: HTTPS_TLS_CERT_PATH=~/cert.pem + +- HTTPS_TLS_KEY_PATH: The path to the TLS private key (pem encoded). + Example: HTTPS_TLS_KEY_PATH=~/key.pem + +- HTTPS_TLS_CERT: A pem encoded TLS certificate passed as string. Can be used instead of HTTPS_TLS_CERT_PATH. + Example: HTTPS_TLS_CERT="-----BEGIN CERTIFICATE-----\nMIIDZTCCAk2gAwIBAgIEV5xOtDANBgkqhkiG9w0BAQ0FADA0MTIwMAYDVQQDDClP..." + +- HTTPS_TLS_KEY: A pem encoded TLS key passed as string. Can be used instead of HTTPS_TLS_KEY_PATH. + Example: HTTPS_TLS_KEY="-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDg..." + + +CORS CONTROLS +============== +- CORS_ALLOWED_ORIGINS: A list of origins (comma separated values) a cross-domain request can be executed from. + If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) + to replace 0 or more characters (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality. + Only one wildcard can be used per origin. The default value is *. + Example: CORS_ALLOWED_ORIGINS=http://*.domain.com,http://*.domain2.com + +- CORS_ALLOWED_METHODS: A list of methods (comma separated values) the client is allowed to use with cross-domain + requests. Default value is simple methods (GET and POST). + Example: CORS_ALLOWED_METHODS=POST,GET,PUT + +- CORS_ALLOWED_CREDENTIALS: Indicates whether the request can include user credentials like cookies, HTTP authentication + or client side SSL certificates. The default is false. + +- CORS_DEBUG: Debugging flag adds additional output to debug server side CORS issues. + +- CORS_MAX_AGE: Indicates how long (in seconds) the results of a preflight request can be cached. The default is 0 which stands for no max age. + +- CORS_ALLOWED_HEADERS: A list of non simple headers (comma separated values) the client is allowed to use with cross-domain requests. + +- CORS_EXPOSED_HEADERS: Indicates which headers (comma separated values) are safe to expose to the API of a CORS API specification. + + +DEBUG CONTROLS +============== + +- PROFILING: Set "PROFILING=cpu" to enable cpu profiling and "PROFILING=memory" to enable memory profiling. + It is not possible to do both at the same time. + Example: PROFILING=cpu + +Usage: + hydra serve [command] + +Available Commands: + admin Serves Administrative HTTP/2 APIs + all Serves both public and administrative HTTP/2 APIs + public Serves Public HTTP/2 APIs + +Flags: + --dangerous-force-http Disable HTTP/2 over TLS (HTTPS) and serve HTTP instead. Never use this in production. + --disable-telemetry Disable anonymized telemetry reports - for more information please visit https://www.ory.sh/docs/guides/telemetry + -h, --help help for serve + --https-tls-cert-path string Path to the certificate file for HTTP/2 over TLS (https). You can set HTTPS_TLS_CERT_PATH or HTTPS_TLS_CERT instead. + --https-tls-key-path string Path to the key file for HTTP/2 over TLS (https). You can set HTTPS_TLS_KEY_PATH or HTTPS_TLS_KEY instead. + +Global Flags: + --config string Config file (default is $HOME/.hydra.yaml) + --skip-tls-verify Foolishly accept TLS certificates signed by unkown certificate authorities + +Use "hydra serve [command] --help" for more information about a command. +``` \ No newline at end of file diff --git a/_legacy/guides/hydra/README.md b/_legacy/guides/hydra/README.md new file mode 100644 index 000000000..fa9ffa6bc --- /dev/null +++ b/_legacy/guides/hydra/README.md @@ -0,0 +1,195 @@ +# Introduction + +Welcome to the Hydra documentation. This documentation will + +1. teach you what OAuth2 and OpenID Connect are and how ORY Hydra fits in the picture. +2. help you run a ORY Hydra installation on your system using Docker. +3. teach you how to install, configure, run and use ORY Hydra. + +Let us begin with the first part, understanding what OAuth2 and OpenID Connect are. + +## What is ORY Hydra? + +ORY Hydra is an OAuth 2.0 and OpenID Connect Provider. As such, it is capable of issuing access, refresh, and ID Tokens. +Contrary to other projects out there, ORY Hydra does not offer user management (login, logout, profile management, +registration) but instead uses a redirection-based flow and a REST API to delegate user authentication (login) to +a service which you implement and control. This allows you to build a user management that works for you, with the frontend +technology that you like, and authentication mechanisms required by your use case (e.g. token-based 2FA, SMS 2FA). + +As such, ORY Hydra is the most flexible OAuth 2.0 and OpenID Connect provider out there and gives you great freedom +in implementing your business logic, and still getting all the benefits from OAuth 2.0 and OpenID Connect. + +Additional to the OAuth 2.0 functionality, ORY Hydra offers a safe storage for cryptographic keys (e.g. for signing JSON Web Tokens) +and is capable of managing OAuth 2.0 Clients. + +ORY Hydra is OpenID Connect certified (pending) and implements all the requirements stated by the OpenID Foundation. As such, +it correctly implements the different OAuth 2.0 and OpenID Connect flows as intended by the IETF and OpenID Foundation. + +## Introduction to OAuth 2.0 and OpenID Connect + +This section will give you some ideas of what OAuth 2.0 and OpenID Connect 1.0 are for. If you +already know what OAuth2 and OpenID Connect are and how they work, you can skip to the next [Section](#introduction-to-hydra). +This section will not explain how the various flows of OAuth2 work and how they look like. We strongly recommend +to read the following articles: + +* [DigitalOcean: An Introduction to OAuth 2](https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2) +* [Aaron Parecki: OAuth2 Simplified](https://aaronparecki.com/2012/07/29/2/oauth2-simplified) +* [Zapier: Chapter 5: Authentication, Part 2](https://zapier.com/learn/apis/chapter-5-authentication-part-2/) + +Please be aware that we do not aim to explain OAuth 2.0 in great detail. We assume that you have some knowledge +of the flows and strongly encourage you to refresh your knowledge with the articles listed above. + +### What is OAuth 2.0? + +[The OAuth 2.0 authorization framework](https://tools.ietf.org/html/rfc6749) is a memo in the +[Request for Comments](https://www.ietf.org/rfc.html) document series published by the +IETF Internet Engineering Task Force (IETF). Memos in the Requests for Comments (RFC) document series +contain technical and organizational notes about the Internet. They cover many aspects of computer +networking, including protocols, procedures, programs, and concepts [...]. + +The OAuth 2.0 authorization framework enables a third-party +application to obtain limited access to an HTTP service, either on +behalf of a resource owner by orchestrating an approval interaction +between the resource owner and the HTTP service, or by allowing the +third-party application to obtain access on its own behalf. + +In the traditional client-server authentication model, the client +requests an access-restricted resource (protected resource) on the +server by authenticating with the server using the resource owner's +credentials. In order to provide third-party applications access to +restricted resources, the resource owner shares its credentials with +the third party. This creates several problems and limitations. + +OAuth addresses these issues by introducing an authorization layer +and separating the role of the client from that of the resource +owner. In OAuth, the client requests access to resources controlled +by the resource owner and hosted by the resource server, and is +issued a different set of credentials than those of the resource +owner. + +Instead of using the resource owner's credentials to access protected +resources, the client obtains an access token -- a string denoting a +specific scope, lifetime, and other access attributes. Access tokens +are issued to third-party clients by an authorization server with the +approval of the resource owner. The client uses the access token to +access the protected resources hosted by the resource server. + +Source: [IETF RFC 6749](https://tools.ietf.org/html/rfc6749) + +### OAuth 2.0 Example + +An end-user (resource owner) can grant a printing +service (client) access to her protected photos stored at a photo- +sharing service (resource server), without sharing her username and +password with the printing service. Instead, she authenticates +directly with a server trusted by the photo-sharing service +(authorization server), which issues the printing service delegation- +specific credentials (access token). + +Source: [IETF RFC 6749](https://tools.ietf.org/html/rfc6749) + +### What is OpenID Connect 1.0? + +OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. +It enables Clients to verify the identity of the End-User based on the authentication performed +by an Authorization Server, as well as to obtain basic profile information about the End-User in +an interoperable and REST-like manner. + +As background, the OAuth 2.0 Authorization Framework and OAuth 2.0 Bearer Token +Usage specifications provide a general framework for third-party +applications to obtain and use limited access to HTTP resources. +They define mechanisms to obtain and use Access Tokens to access resources +but do not define standard methods to provide identity information. +Notably, without profiling OAuth 2.0, it is incapable of providing information +about the authentication of an End-User. + +OpenID Connect implements authentication as an extension to the OAuth 2.0 authorization process. + +Source [OpenID Connect Core 1.0](openid.net/specs/openid-connect-core-1_0.html) + +OpenID Connect allows clients of all types, including Web-based, mobile, and JavaScript clients, +to request and receive information about authenticated sessions and end-users. The specification +suite is extensible, allowing participants to use optional features such as encryption of identity data, +discovery of OpenID Providers, and session management, when it makes sense for them. + +There are different work flows for OpenID Connect 1.0, we recommend checking out the OpenID Connect sandbox at +[openidconnect.net](https://openidconnect.net/). + +## Introduction to Hydra + +Hydra is a server implementation of the OAuth 2.0 authorization framework and the OpenID Connect Core 1.0. Existing OAuth2 +implementations usually ship as libraries or SDKs such as [node-oauth2-server](https://github.com/oauthjs/node-oauth2-server) +or [fosite](https://github.com/ory/fosite), or as fully featured identity solutions with user +management and user interfaces, such as [Keycloak](https://www.keycloak.org/) or [Okta](https://www.okta.com/). + +Implementing and using OAuth2 without understanding the whole specification is challenging and prone to errors, even when +SDKs are being used. The primary goal of Hydra is to make OAuth 2.0 and OpenID Connect 1.0 less painful to set up and easier to use. + +Hydra implements the flows described in OAuth2 and OpenID Connect 1.0 without forcing you to use a "Hydra User Management" +or some template engine or a predefined front-end. Instead it relies on HTTP redirection and cryptographic methods +to verify user consent allowing you to use Hydra with any authentication endpoint, be it [authboss](https://github.com/go-authboss/authboss), +[auth0.com](https://auth0.com/) or your proprietary PHP authentication. + +Hydra incorporates best practices in the area of the web service technology: + +1. Hydra ships as a single binary for all popular platforms including Linux, OSX and Windows, without any additional +dependencies. For further simplicity, Hydra is available as a [Docker Image](https://hub.docker.com/r/oryd/hydra/). +2. Hydra is built security first: architecture and work flows are designed to neutralize various common (OWASP TOP TEN) +and uncommon attack vectors. [Learn more](https://www.ory.sh/docs/guides/master/hydra/5-security/). +3. Hydra has a low CPU and memory footprint, short start up times and a CLI with developers in mind. +4. Hydra scales effortlessly up and down on every platform imaginable, including Heroku, Cloud Foundry, Docker, +Google Container Engine and many more. + +Hydra has a limitations too: + +1. Hydra is not something that manages user accounts. Hydra does not offer user registration, password reset, user +login, sending confirmation emails. This is what the *Identity Provider* is responsible for. +The communication between Hydra and the Identity Provider is called [*Login and Consent Flow*](https://www.ory.sh/docs/guides/master/hydra/3-overview/1-oauth2#implementing-a-login--consent-provider). +2. If you are building a simple service for 50-100 registered users, OAuth2 and Hydra will probably be too sophisticated. +3. Hydra will not support the OAuth2 resource owner password credentials flow. This flow is legacy, discouraged, +and insecure. + +OAuth2 is used in many areas, for various purposes and supported by all well known programming languages, but it is important +to understand what the vision of OAuth2 is. This non-exclusive list might help you decide, if OAuth 2.0 and Hydra are +the right fit for you. + +1. If you want to allow third-party developers accessing your APIs now or in the future, Hydra is the perfect fit. This is what an OAuth2 Provider does. +2. If you want to become a Identity Provider, like Google, Facebook or Microsoft, OpenID Connect and thus Hydra is a perfect fit. +3. Running an OAuth2 Provider works great with browser, mobile and wearable apps, as you can avoid storing access +credentials on the device, phone or wearable and revoke access tokens, and thus access privileges, at any time. +4. If you have a lot of services and want to limit automated access (think: cronjobs) for those services, +OAuth2 might make sense for you. Example: The comment service is not allowed to read user passwords when fetching +the latest user profile updates. + +# OAuth 2.0 Case Study + +OAuth2 and OpenID Connect are tricky to understand. It is important to understand that OAuth2 is +a delegation protocol. It makes sense to use Hydra in new and existing projects. A use case covering an existing project +explains how one would use Hydra in a new one as well. So let's look at a use case! + +Let's assume we are running a ToDo List App (todo24.com). ToDo24 has a login endpoint (todo24.com/login). +The login endpoint is written in node and uses MongoDB to store user information (email + password + settings). Of course, +todo24 has other services as well: list management (todo24.com/lists/manage: close, create, move), +item management (todo24.com/lists/items/manage: mark solved, add), and so on. You are using cookie-based sessions to see which +user is performing the request. + +Now you decide to use OAuth2 on top of your current infrastructure. There are many reasons to do this: +* You want to open your APIs to third-party developers. Their apps will be using OAuth2 Access Tokens to access a user's to do list. +* You want more client applications, for example browser app (SPA), mobile app, car, ... +* You have Cross Origin Requests. Making cookies work with Cross Origin Requests weakens or even disables important anti-CSRF measures. + +These are only a couple of reasons to use OAuth2. You might decide to use OAuth2 as your single source of authorization, thus maintaining +only one authorization protocol and being able to open up to third party devs in no time. With OpenID Connect, you are able to delegate authentication as well as authorization! + +Your decision is final. You want to use OAuth2 and you want Hydra to do the job. You install Hydra in your cluster using docker. +Next, you set up some exemplary OAuth2 clients. Clients can act on their own, but most of the time they need to access a user's todo lists. +To do so, the client initiates an OAuth2 request. This is where the [user login & consent flow](https://www.ory.sh/docs/guides/master/hydra/3-overview/1-oauth2) comes into play. +Before Hydra can issue an access token, we need to know WHICH user is giving consent. To do so, Hydra redirects the user agent (e.g. browser, mobile device) +to the login endpoint alongside with a challenge that contains important request information. The login endpoint (todo24.com/login) authenticates the +user as usual, e.g. by username & password, session cookie or other means. Upon successful authentication, the login endpoint +redirects the user back to ORY Hydra. Next, ORY Hydra needs the user's consent for which the user agent is redirected to the consent endpoint (todo24.com/consent) +where the user is asked for consent: *"Do you want to grant MyCoolAnalyticsApp read & write access to all your todo lists? [Yes] [No]"*. Once the user clicks *Yes* and gives consent, +the consent endpoint redirects back to ORY Hydra which then validates the request and finally issues the access, refresh, and ID tokens. + +You can validate the access tokens which are sent to your API directly at ORY Hydra, or use an Identity & Access Proxy +like ORY Oathkeeper to do it for you. diff --git a/_legacy/guides/hydra/images/abstract_flow.png b/_legacy/guides/hydra/images/abstract_flow.png new file mode 100644 index 000000000..f57769ced Binary files /dev/null and b/_legacy/guides/hydra/images/abstract_flow.png differ diff --git a/_legacy/guides/hydra/images/consent-flow.svg b/_legacy/guides/hydra/images/consent-flow.svg new file mode 100644 index 000000000..21da99bb4 --- /dev/null +++ b/_legacy/guides/hydra/images/consent-flow.svg @@ -0,0 +1,360 @@ +User AgentClient AppHydraConsent AppProtected APIWants to do something that requires authorizationRedirect to http://hydra.mydomain/oauth2/auth?client_id=...Calls http://hydra.mydomain/oauth2/auth?client_id=...Requires authentication and redirects to http://login.mydomain/login?consent=jfu3...Calls http://login.mydomain/login?consent=jfu3...Validates user credentials / session and logs user in.Uses a REST API to ask for details of the consent request.Shows a screen where the user is asked which OAuth2 scopes (e.g.: "Access to my fotos") should be granted.Users a REST API to confirm the consent request.Redirects back to http://hydra.mydomain/oauth2/auth?client_id=...Calls http://hydra.mydomain/oauth2/auth?client_id=...&consent=jfu3...Validates the consent request and if valid, generates access tokens / authorize codes.Transmits tokens / codes to the client app.Makes authorized request using the access token.Validates the access token using Hydra's REST APIUser AgentClient AppHydraConsent AppProtected API \ No newline at end of file diff --git a/_legacy/guides/hydra/images/consent-state.svg b/_legacy/guides/hydra/images/consent-state.svg new file mode 100644 index 000000000..31379bba9 --- /dev/null +++ b/_legacy/guides/hydra/images/consent-state.svg @@ -0,0 +1,360 @@ +
Redirects to consent app with consent request ID
Initiates Consent Flow
yes
no
Sign in failed
Sign in successful
User denies authorization
User accepts authorization
Redirect to redirectUrl value
Hydra
Consent App
Is the user signed in already?
Fetch consent request from Hydra using REST API
Sign user in using login form
Ask user to authorize requested scopes
Tell Hydra to deny the consent request using REST API
Tell Hydra to accept the consent request with the granted scopes using REST API
Read redirectUrl value from consent request payload
Hydra
\ No newline at end of file diff --git a/_legacy/guides/hydra/images/consent.png b/_legacy/guides/hydra/images/consent.png new file mode 100644 index 000000000..184dd779b Binary files /dev/null and b/_legacy/guides/hydra/images/consent.png differ diff --git a/_legacy/guides/hydra/images/gliffy/hydra-arch.gliffy b/_legacy/guides/hydra/images/gliffy/hydra-arch.gliffy new file mode 100644 index 000000000..298c127bd --- /dev/null +++ b/_legacy/guides/hydra/images/gliffy/hydra-arch.gliffy @@ -0,0 +1 @@ +{"contentType":"application/gliffy+json","version":"1.1","metadata":{"title":"untitled","revision":0,"exportBorder":false},"embeddedResources":{"index":0,"resources":[]},"stage":{"objects":[{"x":373,"y":328,"rotation":0,"id":55,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":55,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":1,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[-3,2],[122,2],[122,59.5],[247,59.5]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":2,"px":1,"py":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":42,"px":0,"py":0.5}}},"linkMap":[]},{"x":329.79999999999995,"y":207.5,"rotation":0,"id":48,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","width":80,"height":30,"lockAspectRatio":false,"lockShape":false,"order":48,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":1.5999999999999999,"y":0,"rotation":0,"id":50,"uid":null,"width":76.8,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Docker

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":250,"y":222.5,"rotation":0,"id":46,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","width":140,"height":200,"lockAspectRatio":false,"lockShape":false,"order":0,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":325,"y":382,"rotation":0,"id":30,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":16,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":1,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[-5,-2],[-5,105.5],[-195,105.5]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":31,"uid":null,"width":123,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

snapshots for recovery

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":2,"px":0.5,"py":1}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":11,"px":1,"py":0.5}}},"linkMap":[]},{"x":80,"y":367,"rotation":0,"id":27,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":14,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":1,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":false,"interpolationType":"linear","cornerRadius":null,"controlPath":[[0,0.5],[0,83]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":32,"uid":null,"width":93,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

create snapshots

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":16,"px":0.5,"py":1}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":11,"px":0.5,"py":0}}},"linkMap":[]},{"x":197,"y":321,"rotation":0,"id":22,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":12,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":1,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[73,9],[26.333333333333314,9],[-20.333333333333343,9],[-67,9]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":23,"uid":null,"width":44,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

pub/sub

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":2,"px":0,"py":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":16,"px":1,"py":0.5}}},"linkMap":[]},{"x":30,"y":292.5,"rotation":0,"id":16,"uid":"com.gliffy.shape.flowchart.flowchart_v1.default.database","width":100,"height":75,"lockAspectRatio":false,"lockShape":false,"order":10,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.database.flowchart_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":18,"uid":null,"width":96,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Message Broker

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":30,"y":450,"rotation":0,"id":11,"uid":"com.gliffy.shape.flowchart.flowchart_v1.default.database","width":100,"height":75,"lockAspectRatio":false,"lockShape":false,"order":8,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.database.flowchart_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":13,"uid":null,"width":96,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Datastore

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":617,"y":332,"rotation":0,"id":8,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":6,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":1,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[3,-72.90620433565948],[-122,-72.90620433565948],[-122,-2],[-247,-2]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":9,"uid":null,"width":66,"height":28,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

HTTP REST\n

OAuth2

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":42,"px":1.1102230246251563e-16,"py":0.2928932188134525}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":2,"px":1,"py":0.5}}},"linkMap":[]},{"x":270,"y":280,"rotation":0,"id":2,"uid":"com.gliffy.shape.flowchart.flowchart_v1.default.multiple_documents","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":4,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.multiple_documents.flowchart_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":4,"uid":null,"width":96,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Hydra Host

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":810,"y":62.5,"rotation":0,"id":51,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","width":70,"height":40,"lockAspectRatio":false,"lockShape":false,"order":51,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":53,"uid":null,"width":66,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Clients

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":665,"y":540,"rotation":0,"id":40,"uid":"com.gliffy.shape.network.network_v3.business.server","width":77,"height":120,"lockAspectRatio":true,"lockShape":false,"order":20,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.server_3d.network_v3","strokeWidth":2,"strokeColor":"#000000","fillColor":"#003366","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":665,"y":387.5,"rotation":0,"id":38,"uid":"com.gliffy.shape.network.network_v3.business.workstation_lcd","width":120,"height":120,"lockAspectRatio":true,"lockShape":false,"order":19,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.workstation_lcd_3d.network_v3","strokeWidth":2,"strokeColor":"#000000","fillColor":"#003366","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":665,"y":90,"rotation":0,"id":36,"uid":"com.gliffy.shape.network.network_v3.home.laptop","width":120,"height":120,"lockAspectRatio":true,"lockShape":false,"order":18,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.laptop_3d.network_v3","strokeWidth":2,"strokeColor":"#000000","fillColor":"#003366","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":665,"y":240,"rotation":0,"id":5,"uid":"com.gliffy.shape.ui.ui_v2.forms_components.window","width":120,"height":100,"lockAspectRatio":false,"lockShape":false,"order":2,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.window.ui_v2","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":1.3333333333333333,"y":0,"rotation":0,"id":7,"uid":null,"width":117.33333333333336,"height":70,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"

\n

\n

\n

\n

Hydra CLI

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":620,"y":77.5,"rotation":0,"id":42,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","width":210,"height":620,"lockAspectRatio":false,"lockShape":false,"order":1,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]}],"background":"#FFFFFF","width":880,"height":698,"maxWidth":5000,"maxHeight":5000,"nodeIndex":57,"autoFit":true,"exportBorder":false,"gridOn":true,"snapToGrid":true,"drawingGuidesOn":true,"pageBreaksOn":false,"printGridOn":false,"printPaper":"LETTER","printShrinkToFit":false,"printPortrait":true,"shapeStyles":{"com.gliffy.shape.basic.basic_v1.default":{"fill":"#FFFFFF","stroke":"#333333","strokeWidth":2},"com.gliffy.shape.flowchart.flowchart_v1.default":{"fill":"#FFFFFF","stroke":"#333333","strokeWidth":2},"com.gliffy.shape.network.network_v3.home":{"fill":"#003366"},"com.gliffy.shape.network.network_v3.business":{"fill":"#003366"}},"lineStyles":{"global":{"startArrow":1,"endArrow":1}},"textStyles":{},"themeData":null}} \ No newline at end of file diff --git a/_legacy/guides/hydra/images/gliffy/hydra.gliffy b/_legacy/guides/hydra/images/gliffy/hydra.gliffy new file mode 100644 index 000000000..7b8f95a92 --- /dev/null +++ b/_legacy/guides/hydra/images/gliffy/hydra.gliffy @@ -0,0 +1 @@ +{"contentType":"application/gliffy+json","version":"1.1","metadata":{"title":"untitled","revision":0,"exportBorder":false},"embeddedResources":{"index":0,"resources":[]},"stage":{"objects":[{"x":651.5405032467534,"y":529,"rotation":0,"id":120,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","width":239.4375,"height":70,"lockAspectRatio":false,"lockShape":false,"order":120,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":122,"uid":null,"width":235.4375,"height":56,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

You can use Hydra with any Identity Provider. You must implement the capability to issue consent tokens using JWT.

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":907.45,"y":224,"rotation":0,"id":117,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","width":110,"height":40,"lockAspectRatio":false,"lockShape":false,"order":117,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":119,"uid":null,"width":106,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Identity Provider

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":178.45000000000005,"y":273,"rotation":0,"id":112,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":112,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[2.137084989847608,2],[2.137084989847608,-90.5],[114,-90.5],[114,-163]],"lockSegments":{"1":true}}},"children":[{"x":0,"y":0,"rotation":0,"id":113,"uid":null,"width":157,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

5. Receive Access / ID Token

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":93,"px":0.7071067811865476,"py":0}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":4,"px":0.5,"py":1}}},"linkMap":[]},{"x":418.01250000000005,"y":241.5,"rotation":0,"id":103,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","width":500,"height":380,"lockAspectRatio":false,"lockShape":false,"order":0,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":760.45,"y":415,"rotation":0,"id":99,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":42,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[157.5625,-62.20057685088807],[197.5625,-62.20057685088807],[197.5625,270],[-613,270],[-613,230]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":100,"uid":null,"width":177,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

4. Return Signed Consent Token

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":103,"px":1,"py":0.29289321881345237}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":93,"px":0.5,"py":1}}},"linkMap":[]},{"x":0.537500000000037,"y":630,"rotation":0,"id":95,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","width":133.82500000000002,"height":30,"lockAspectRatio":false,"lockShape":false,"order":40,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2.6765000000000003,"y":0,"rotation":0,"id":97,"uid":null,"width":128.47200000000004,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Authorization Service

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":67.45000000000005,"y":275,"rotation":0,"id":93,"uid":"com.gliffy.shape.basic.basic_v1.default.rectangle","width":160,"height":370,"lockAspectRatio":false,"lockShape":false,"order":1,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":221.45000000000005,"y":257,"rotation":0,"id":36,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":16,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[6,126.37049096097735],[101.28125,126.37049096097735],[101.28125,95.79942314911193],[196.56250000000006,95.79942314911193]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":57,"uid":null,"width":140,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

2. Issue Consent Request

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":93,"px":1,"py":0.29289321881345237}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":103,"px":1.1102230246251563e-16,"py":0.2928932188134525}}},"linkMap":[]},{"x":242.45000000000005,"y":76,"rotation":0,"id":34,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":12,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[5,-11],[-128.1370849898476,-11],[-128.1370849898476,199]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":55,"uid":null,"width":137,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

1. Request Access Token

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":4,"px":0,"py":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":93,"px":0.2928932188134524,"py":0}}},"linkMap":[]},{"x":148.2,"y":513,"rotation":0,"id":38,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":18,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[-4.374999999999943,-21.44108899330365],[-4.374999999999943,-58.62739266220245],[-4.374999999999972,-95.8136963311012],[-4.374999999999972,-133]],"lockSegments":{}}},"children":[],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":26,"px":0.5,"py":0}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":23,"px":0.5,"py":1}}},"linkMap":[]},{"x":87.4500000000001,"y":491.5,"rotation":0,"id":26,"uid":"com.gliffy.shape.flowchart.flowchart_v1.default.database","width":120,"height":130,"lockAspectRatio":false,"lockShape":false,"order":8,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.database.flowchart_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2.4,"y":0,"rotation":0,"id":39,"uid":null,"width":115.19999999999999,"height":112,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

\n

\n

Hydra Database (Rethink DB)\n

\n

Client Credentials, Access Tokens, Policies, ...

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":77.44999999999999,"y":290,"rotation":0,"id":23,"uid":"com.gliffy.shape.flowchart.flowchart_v1.default.multiple_documents","width":132.75000000000006,"height":90,"lockAspectRatio":false,"lockShape":false,"order":6,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.multiple_documents.flowchart_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2.655,"y":0,"rotation":0,"id":66,"uid":null,"width":127.44000000000005,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Hydra Host

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":385.08149350649353,"y":-52,"rotation":0,"id":58,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":25,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[-47.631493506493484,117],[282.9310064935065,117],[282.9310064935065,293.5]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":114,"uid":null,"width":117,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

3. Login and Consent

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":4,"px":1,"py":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":103,"px":0.5,"py":0}}},"linkMap":[]},{"x":247.45000000000005,"y":20,"rotation":0,"id":4,"uid":"com.gliffy.shape.network.network_v3.home.laptop","width":90,"height":90,"lockAspectRatio":true,"lockShape":false,"order":4,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.laptop_3d.network_v3","strokeWidth":2,"strokeColor":"#000000","fillColor":"#003366","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":559.6967532467534,"y":425,"rotation":0,"id":81,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":38,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":0,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[-4.368506493506516,-1],[-4.368506493506516,32.333333333333314],[-4.368506493506516,65.66666666666669],[-4.368506493506516,99]],"lockSegments":{}}},"children":[{"x":0,"y":0,"rotation":0,"id":83,"uid":null,"width":96,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"both","vposition":"none","hposition":"none","html":"

Verify Credentials

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":29,"px":0.5,"py":1}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":78,"px":0.5,"py":0}}},"linkMap":[]},{"x":505.32824675324673,"y":524,"rotation":0,"id":78,"uid":"com.gliffy.shape.flowchart.flowchart_v1.default.database","width":100,"height":75,"lockAspectRatio":false,"lockShape":false,"order":36,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.database.flowchart_v1","strokeWidth":2,"strokeColor":"#333333","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":80,"uid":null,"width":96,"height":28,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

LDAP, MySQL, Google, ...

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":654.6967532467534,"y":346,"rotation":0,"id":76,"uid":"com.gliffy.shape.basic.basic_v1.default.line","width":100,"height":100,"lockAspectRatio":false,"lockShape":false,"order":35,"graphic":{"type":"Line","Line":{"strokeWidth":2,"strokeColor":"#000000","fillColor":"none","dashStyle":null,"startArrow":0,"endArrow":1,"startArrowRotation":"auto","endArrowRotation":"auto","ortho":true,"interpolationType":"linear","cornerRadius":10,"controlPath":[[0.6314935064934843,-2],[9.087662337662323,-2],[17.54383116883116,-2],[26,-2]],"lockSegments":{}}},"children":null,"constraints":{"constraints":[],"startConstraint":{"type":"StartPositionConstraint","StartPositionConstraint":{"nodeId":29,"px":1,"py":0.5}},"endConstraint":{"type":"EndPositionConstraint","EndPositionConstraint":{"nodeId":67,"px":0,"py":0.5}}},"linkMap":[]},{"x":691.2592532467534,"y":304,"rotation":0,"id":72,"uid":"com.gliffy.shape.ui.ui_v2.forms_components.text_area","width":178.875,"height":50,"lockAspectRatio":false,"lockShape":false,"order":33,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#8D8D8D","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2.9812500000000006,"y":0,"rotation":0,"id":74,"uid":null,"width":172.91249999999994,"height":42,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"top","overflow":"none","vposition":"none","hposition":"none","html":"

Do you want to grant app foo access to your pictures and email?

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":790.1342532467534,"y":374,"rotation":0,"id":70,"uid":"com.gliffy.shape.ui.ui_v2.forms_components.button","width":80,"height":20,"lockAspectRatio":false,"lockShape":false,"order":31,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":2,"strokeColor":"#8D8D8D","fillColor":"#DADADA","gradient":true,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":71,"uid":null,"width":76,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Deny\n

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":691.2592532467534,"y":374,"rotation":0,"id":68,"uid":"com.gliffy.shape.ui.ui_v2.forms_components.button","width":80,"height":20,"lockAspectRatio":false,"lockShape":false,"order":29,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":2,"strokeColor":"#8D8D8D","fillColor":"#DADADA","gradient":true,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":69,"uid":null,"width":76,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Allow

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":680.6967532467534,"y":264,"rotation":0,"id":67,"uid":"com.gliffy.shape.ui.ui_v2.forms_components.window","width":200,"height":160,"lockAspectRatio":false,"lockShape":false,"order":28,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.window.ui_v2","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]},{"x":495.32824675324684,"y":344,"rotation":0,"id":44,"uid":"com.gliffy.shape.ui.ui_v2.forms_components.textbox","width":120,"height":20,"lockAspectRatio":false,"lockShape":false,"order":23,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#8D8D8D","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":47,"uid":null,"width":116,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Password

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":495.32824675324684,"y":319,"rotation":0,"id":42,"uid":"com.gliffy.shape.ui.ui_v2.forms_components.textbox","width":120,"height":20,"lockAspectRatio":false,"lockShape":false,"order":21,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.rectangle.basic_v1","strokeWidth":2,"strokeColor":"#8D8D8D","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":46,"uid":null,"width":116,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

User

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":535.3282467532468,"y":374,"rotation":0,"id":40,"uid":"com.gliffy.shape.ui.ui_v2.forms_components.button","width":80,"height":20,"lockAspectRatio":false,"lockShape":false,"order":19,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.round_rectangle.basic_v1","strokeWidth":2,"strokeColor":"#8D8D8D","fillColor":"#DADADA","gradient":true,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[{"x":2,"y":0,"rotation":0,"id":41,"uid":null,"width":76,"height":14,"lockAspectRatio":false,"lockShape":false,"order":"auto","graphic":{"type":"Text","Text":{"tid":null,"valign":"middle","overflow":"none","vposition":"none","hposition":"none","html":"

Log in

","paddingLeft":2,"paddingRight":2,"paddingBottom":2,"paddingTop":2}},"children":null}],"linkMap":[]},{"x":455.32824675324684,"y":264,"rotation":0,"id":29,"uid":"com.gliffy.shape.ui.ui_v2.forms_components.window","width":200,"height":160,"lockAspectRatio":false,"lockShape":false,"order":2,"graphic":{"type":"Shape","Shape":{"tid":"com.gliffy.stencil.window.ui_v2","strokeWidth":2,"strokeColor":"#000000","fillColor":"#FFFFFF","gradient":false,"dropShadow":false,"state":0,"shadowX":0,"shadowY":0,"opacity":1}},"children":[],"linkMap":[]}],"background":"#FFFFFF","width":1018,"height":692,"maxWidth":5000,"maxHeight":5000,"nodeIndex":124,"autoFit":true,"exportBorder":false,"gridOn":true,"snapToGrid":true,"drawingGuidesOn":true,"pageBreaksOn":false,"printGridOn":false,"printPaper":"LETTER","printShrinkToFit":false,"printPortrait":true,"shapeStyles":{"com.gliffy.shape.network.network_v3.home":{"fill":"#003366"},"com.gliffy.shape.basic.basic_v1.default":{"fill":"#FFFFFF","stroke":"#333333","strokeWidth":2},"com.gliffy.shape.network.network_v3.business":{"fill":"#003366"},"com.gliffy.shape.flowchart.flowchart_v1.default":{"fill":"#FFFFFF","stroke":"#333333","strokeWidth":2}},"lineStyles":{"global":{"endArrow":1,"startArrow":0}},"textStyles":{},"themeData":null}} \ No newline at end of file diff --git a/_legacy/guides/hydra/images/google.png b/_legacy/guides/hydra/images/google.png new file mode 100644 index 000000000..9ccc6e6fd Binary files /dev/null and b/_legacy/guides/hydra/images/google.png differ diff --git a/_legacy/guides/hydra/images/google2.png b/_legacy/guides/hydra/images/google2.png new file mode 100644 index 000000000..0e908b669 Binary files /dev/null and b/_legacy/guides/hydra/images/google2.png differ diff --git a/_legacy/guides/hydra/images/hydra-arch-warden.png b/_legacy/guides/hydra/images/hydra-arch-warden.png new file mode 100644 index 000000000..890dfb7e1 Binary files /dev/null and b/_legacy/guides/hydra/images/hydra-arch-warden.png differ diff --git a/_legacy/guides/hydra/images/hydra-authentication.gif b/_legacy/guides/hydra/images/hydra-authentication.gif new file mode 100644 index 000000000..b6f789dee Binary files /dev/null and b/_legacy/guides/hydra/images/hydra-authentication.gif differ diff --git a/_legacy/guides/hydra/images/insecure-connection.png b/_legacy/guides/hydra/images/insecure-connection.png new file mode 100644 index 000000000..ee84371d3 Binary files /dev/null and b/_legacy/guides/hydra/images/insecure-connection.png differ diff --git a/_legacy/guides/hydra/images/install-result.png b/_legacy/guides/hydra/images/install-result.png new file mode 100644 index 000000000..29086d44e Binary files /dev/null and b/_legacy/guides/hydra/images/install-result.png differ diff --git a/_legacy/guides/hydra/images/login-consent-flow.png b/_legacy/guides/hydra/images/login-consent-flow.png new file mode 100644 index 000000000..8008538bf Binary files /dev/null and b/_legacy/guides/hydra/images/login-consent-flow.png differ diff --git a/_legacy/guides/hydra/images/login-success-a.gif b/_legacy/guides/hydra/images/login-success-a.gif new file mode 100644 index 000000000..7e5c32986 Binary files /dev/null and b/_legacy/guides/hydra/images/login-success-a.gif differ diff --git a/_legacy/guides/hydra/images/logo-essential.png b/_legacy/guides/hydra/images/logo-essential.png new file mode 100644 index 000000000..af6bfb93e Binary files /dev/null and b/_legacy/guides/hydra/images/logo-essential.png differ diff --git a/_legacy/guides/hydra/images/logo.png b/_legacy/guides/hydra/images/logo.png new file mode 100644 index 000000000..916d9d76f Binary files /dev/null and b/_legacy/guides/hydra/images/logo.png differ diff --git a/_legacy/guides/hydra/images/oauth2-flow.gif b/_legacy/guides/hydra/images/oauth2-flow.gif new file mode 100644 index 000000000..7054ddea9 Binary files /dev/null and b/_legacy/guides/hydra/images/oauth2-flow.gif differ diff --git a/_legacy/guides/hydra/images/run-the-example.gif b/_legacy/guides/hydra/images/run-the-example.gif new file mode 100644 index 000000000..85f02c490 Binary files /dev/null and b/_legacy/guides/hydra/images/run-the-example.gif differ diff --git a/_legacy/guides/hydra/images/sample_trace.png b/_legacy/guides/hydra/images/sample_trace.png new file mode 100644 index 000000000..51987091d Binary files /dev/null and b/_legacy/guides/hydra/images/sample_trace.png differ diff --git a/_legacy/guides/hydra/images/social-login-example.jpg b/_legacy/guides/hydra/images/social-login-example.jpg new file mode 100644 index 000000000..e3f737d5e Binary files /dev/null and b/_legacy/guides/hydra/images/social-login-example.jpg differ diff --git a/_legacy/guides/hydra/images/social-login-example.png b/_legacy/guides/hydra/images/social-login-example.png new file mode 100644 index 000000000..10f1ea262 Binary files /dev/null and b/_legacy/guides/hydra/images/social-login-example.png differ diff --git a/_legacy/guides/hydra/images/social-login.png b/_legacy/guides/hydra/images/social-login.png new file mode 100644 index 000000000..f4f4016da Binary files /dev/null and b/_legacy/guides/hydra/images/social-login.png differ diff --git a/_legacy/guides/hydra/images/sponsors/auth0.png b/_legacy/guides/hydra/images/sponsors/auth0.png new file mode 100644 index 000000000..8b49dc1fd Binary files /dev/null and b/_legacy/guides/hydra/images/sponsors/auth0.png differ diff --git a/_legacy/guides/hydra/metrics/telemetry-example.json b/_legacy/guides/hydra/metrics/telemetry-example.json new file mode 100644 index 000000000..b13791fe7 --- /dev/null +++ b/_legacy/guides/hydra/metrics/telemetry-example.json @@ -0,0 +1,58 @@ +[ + { + "context": { + "ip": "0.0.0.0", + "library": { + "name": "analytics-go", + "version": "3.0.0" + } + }, + "messageId": "21999137-d1d2-4102-9a94-57beed5e5fca", + "timestamp": "2018-01-18T18:41:37.028Z", + "traits": { + "buildTime": "2018-01-18 18:41:35.6222348 +0000 UTC", + "goarch": "amd64", + "goos": "windows", + "hash": "undefined", + "instanceId": "c2bdd39c-3b0a-4f3d-b394-8e51f23833c4", + "numCpu": 8, + "runtimeVersion": "go1.9", + "version": "dev-master" + }, + "type": "identify", + "userId": "22b137b6aae9bc40feb7ff14a08a1b9ecbc6305f77956214404c5b744c3b3fe2", + "writeKey": "yF6PTASTliRjCtRbUnwgsvjrvneqACDM", + "sentAt": "2018-01-18T18:41:42.546Z", + "integrations": {}, + "receivedAt": "2018-01-18T18:41:41.972Z", + "originalTimestamp": "2018-01-18T19:41:37.6027834+01:00" + }, + { + "context": { + "ip": "0.0.0.0", + "library": { + "name": "analytics-go", + "version": "3.0.0" + } + }, + "messageId": "258f0127-498a-4d71-8c55-ce678a5d92b8", + "name": "/clients", + "properties": { + "latency": 0, + "method": "GET", + "name": "/clients", + "path": "/clients", + "size": 154, + "status": 401, + "url": "http://22b137b6aae9bc40feb7ff14a08a1b9ecbc6305f77956214404c5b744c3b3fe2/clients" + }, + "timestamp": "2018-01-18T18:41:49.537Z", + "type": "page", + "userId": "22b137b6aae9bc40feb7ff14a08a1b9ecbc6305f77956214404c5b744c3b3fe2", + "writeKey": "yF6PTASTliRjCtRbUnwgsvjrvneqACDM", + "sentAt": "2018-01-18T18:41:52.547Z", + "integrations": {}, + "receivedAt": "2018-01-18T18:41:51.380Z", + "originalTimestamp": "2018-01-18T19:41:50.7046198+01:00" + } +] \ No newline at end of file diff --git a/_legacy/guides/images/basic-oauth2-system.png b/_legacy/guides/images/basic-oauth2-system.png new file mode 100644 index 000000000..d72042e73 Binary files /dev/null and b/_legacy/guides/images/basic-oauth2-system.png differ diff --git a/_legacy/guides/images/ory-ecosystem.png b/_legacy/guides/images/ory-ecosystem.png new file mode 100644 index 000000000..e38c16be0 Binary files /dev/null and b/_legacy/guides/images/ory-ecosystem.png differ diff --git a/_legacy/guides/images/ory-ecosystem.xml b/_legacy/guides/images/ory-ecosystem.xml new file mode 100644 index 000000000..46992e95c --- /dev/null +++ b/_legacy/guides/images/ory-ecosystem.xml @@ -0,0 +1 @@ +7L3ZkrPIli74NPu2DQkk0KUzz2LSgO4ASQghBgkhQE/fPhEZf9auc6qPVVdbm2Vus50RhOPDGr81OPkvVqpG7ZW0N6c5Xx7/WjLn8V+s/K8l/Idfw3+hJxN5wvMCeZC/ijN5tPjrQVh8L/QhQ5/2xfnS/THw3TSPd9H++TBr6vqSvf94lrxezfDnsGvz+HPVNskv/+FBmCWP//j0UJzfN/JUWDF/PdcvRX6bV14w9C9pkpX5q+lrut6/luwV/0P+XCXzXHR8d0vOzfDrEav8i5VeTfMmP1WjdHkg2s5kI++p/8lff/b9utTv/8oLLHnhkzz6y7xjvK/3NNMCn+aCxi/+xYrDrXhfwjbJ0F8HyH347PauHvTPdLrL630Z/9MtLX4OCgXo0lSX92uCQ+gLnEBpQ2WHnWk1/MWJxTzm9psL88CEcj//mfsvCsAfKBH+PUGW/4Yg6wdcQTwXH/hjjn4EngHHKPW5bQp4qCWzmAfB6X+N+1/QkvkfoeVq8Tdacv+Glsy/oSX730BK7v+IlMv/v5By8z9IytV/IGXc9C9sAM+X/89Js2b+RhrmP5Lm3ynsfwdlFv9OYf9GD2SQ2//0nNSHJOk8nPl/ev6/W6yf3/93Fkv4byDA5n+tZUzyKPIa/2H97JFbETN4rMvrr99nFdsGMRyvT+dX8p/p4P/hxKCHZICq/X/B8zLeq/lA747GJRUSxjrtWjyamRdIX39f8r9sDP4LPmre+Isw4r+oAct/LwL/EywW/vciDnFEi34sKoxsRHSaAuIZO0kvD6/pinfRoDOnzfvdVHDAA/1B/EErUvNoXniqGa/8NQeg9Ho3iJBJ1xLEdS1GRHERLwnmp8z8BP58Tt7Jv1hAfl2qbZ3/aykVeyhoA2NpeQPgP264uym7HACpNOCvrSiBGD2/6+zBQT9IR9E4HOGPEneHv/bGoHJttFnCn3OgPBR/H3DL/pv4PgMMICuOg+i5Yjy9fKQS+/qkcg7++keRg3L49TuQGqc0pN9PDruT8ft3w+kMyf/1QPNPhq/8GgA6Y/o9QPJP8e8BAHTNHwOAb8aB8ntfTj75+e9lxVNQ/h4A8mnIzd8PTmJ5A79mzaXh/scAU3z8MQBI058DRPC4KeHvJ5NsSuD3supD+2OAJNsSKH/PUWqhIv4aAGzJ+T0AlNrh9wAAbOvPAYp0UILfZ5eNf3jy1z//8OTXg3948uvBPzz5/eQfnvx68A9Pfj34hye/n/zDk18P/uHJrwf/8OT3k3948uvBPzz59eAfnvx+8g9Pfj34hye/HvzDk99P/uHJrwf/8OTXg3948vvJPzz59eAfnvx68A9Pfj/5hye/Hvy/yhM579grG2qry36wtPs6q9TvBSgtUziHTP3XUnwN8tN/rp7iwTh2OvPSpEY93S3lDF6abqiPm1W6eZca6hmIQuR/w2Stff+1VK/fL1t7pbAP8+rBAZ+L3cMrG8jRDYm5/bkfSHvxjzMa4d/O1Ep/459rhH+jSyv9TQbecMgftFXhkN8P/sMQyYeE+WNIqPyNx5b/N3mWQ+VvcpL4f6O/DYf8HqCIcMifPPz7kNKEgvJHfe/2N5mX8JDf70Cq/qk3Wmn+KY9/J7wIDnDI7yf/8OYf3vy38GbbQTNwp4M0x133rJAo2aa/nvgMWpbQ+xjwXxY7wP+/uunLXadLXDkHg32EzwRPvwcLhy6wnth+fQ2c6IvGn70TnJ1/fZirMnE3zo4bQkqJ+5GAb51wcFCdR0oEX9kKJnpFgf8vSpN5oSeS86NCqbwVS2O4+OehLPAUhvSdKZMrZ1dX5IsZ52Qdhavmdfj7/gv06mhQTlxMn0qHzE2dIa7U5V9rzRzdSt8xt41+CAP8uyMeZ9b62+6Pd5yJSp/udwdfXD0mSmlRJBKlSF6Z/9q3Jd5nCV4rxQg8o/cpt85KPmvM2RJXgC0OBj6gIW0liZy1seUt6ApeNMlRGSjedGu7hV6CyynLqfRbCkO0y5cUaQKZaWDySHARumfRz3N5smfaSDeV7lks1pARLBKE42TBf+WhvxpdhfwVvkdPEG5z37c57mpzY5l0BlkB5M6sk5Jf5Oa0m1dQZj1ToJR2t5MU0gOKP6dgIiCtLOXn/ZxwUgJiXubVZM0U0WbuN+h0VvxzOn9HuagVCCbsZ+q6ij9T11ZKqTIbulkQG0TXxRz4trHLKddtkM969IUkPZyambIH+gLIoVAadlcUdI/abL80yP2u6OfzxeBEJUaKcyCFh1myZHCb17jDySffUUry6wBmGhojlMDyNHPckQxKKzHzfdlU/Z+5pHmuIxw6QW0gc4mDOM9ljiJwKmWeK/uRHinzgWzeAZ1LB9o8l4fnMn7mAsUsPcMtt/xbTmXhYgCqWWKv7OTQUf4aN/NQUaRwaq6cqngXDezIylSy1+YAyi66BFmE7c0pIOtvlIHwTZGOogl2BuVbo71nmq6NRip/1lMZakNlX/SVUg9UIrcu2M9naqEAr0854acB+nkPFpxCOglUlkAT+cRe+1IphXZBrQj0Jg2dyIQaZITRzGdL2dG9ik9QFlwlEWuQi9Ys0NDGSArj50RklJtLfJMIrNw0KmOge5J2s0K0kgRubkl1SzSCeU8ulBUbxLN8W7OqaEUIRCsx4plOj5lOCaSTcsqpJYLjFHoeM4fH8BcilSsXHQP91Em5aEK/TeU7l2bdEuMbfGGa5chWGnJuR+pEU0QvkHPDiYiqSPHNB2FfaIRQmniYbYj5ul74x0YZobkyLKWe5elUaIQpGphma3Q5DpZhCwv5gkwj2Xs465xYQJhrcP4N4GMM0o8RcoIcmLUC2Uj22My0PSoMkPYjNEdopAysWUDUEbq2yvFFOpd6mOfKIh+Y0c9cojrPVSsD/Otfcx1iOpc8WhCVdD9zKRWdS87CAZjuX/uS57muyg5IUWDMc+1musscXN4wJYmQEZIE0ZcQTDRNKZipUsz+Rw9cuPpuNs1Qy8h4S5ShhOYG1Rrfusww6owW1wNRoe9rREJlH+p3yUgqkRIF1PNub4iVsxVydzNywsoET0JOp0o7Km1wI0o5WLOlk9VZ+6Qb/FeL3AzWCB/+iqXIkKHBcuT9LG3TD8cRuywnohwvZ98ugQCEEVjN1gyGinRPoQI9z1fhyJ5MSZn3FPtQ6Z3drPmJyfn0B3jUJ1jxk11Aah/W30vlEMOlMQHFagxcxmTsiRUuD2kl9nfy90KeZwdodt2nJ5ZcRSH6ZcPtSifpQrgm5jHlcw7NnAF0CxGPjKO+SwbYRF9iOs6ccapYwReUtcER+6GBiuiXBF0o/CFtaqrDcUWpGkMjEwK5VxSyRvTXGlB7fXAfqQQYo0Px8wAVugRaBndgEL0CxBZlYAelQjzCA0pEr0oylw4NG5zLmLGDOhrU3g0imkt0IjpXFvrzXAaSsC80khLRK4qKPMQ/H0jzXPIozXON+pPXIXvMCHjVOfGhrFK5HuTlrLksHC4mJrWwW7Alyi4C9DxUMp/ueEYca7MBiiE/iLy7YEPGy74JGQkcqlKG2BMkIoEXtKIgvG2p/DIBRftnJHQ7sSbyKwKXI5bB10pkmdcmR4RuQs4AMw2JgZ5LHOFyIw1E1VTJgjIP9IK6G6eZJfCBzsBJE7H0FnDIXiW/gjsCFkcRCoy4KCa0kFQcxB21tj9RVOkxxb7doeOAcBeIewMSIQfVjK2ZGSNKUKeAmu8bskcD0OjHhZAUrg09AqGnHHhUeqBbhnqqMAHhuOzO0V8UqHBhOZrpk830ER1kQvyCehPo0oh+q8UO0WF3o5JgdBGhg2QjUvtSReiggZFogZjvoXeFBobaFtmJcsKbrwIf++J51rRupOczHbzdBUVqw5YSyAElEohBWs5rTD9rALjGFgNV/PuZxi6x6CNNy2WD/i4H5N0tgCghBxx40t/FJ5lLzg20X01Ucvp7TGMTToJ+Dc7tU7pz2jjPVaK5YiWZ5yrnuXwfzyXOc83ehEPeHcqzQnUlkwTCQw+oiIeMLIr09xk5+QqaS5L271Ef49cZBXqHGNvDnNqVbXGBPAQhVMfvDv7fMb4L7YJw9kM5y0iIiKJfSPM7CXYBucNBqdLAQDE4I1F8qgeQOyXUJJ9IlUhtryKO6NSNtqWnLgC1udj7AH2osOQbskQmEn1onENInQuV/CakNgxOCO20uCVxgQR8ivFKiEuVHHKAUtxSAMV41fsNj9/XylV5YkK51DLC+InKhLGCTowtI+W+jkVoWZ/+PCk22xB5IUIjl4TlTwQG5XOCVxVzh1r6cF7VQGpkwGjGIZSAAROlxB3vwWHoHmY0IcOJIS0UkFC5bArKKbVI4IrQiY7Ef8oBwQyDYmCBOFGUGisqoa5ZnJDeNdJI5EIaAdXtL8IMkEuyMv+OdVsGIz6ffCaeQYYBGSGAIyp4DpHauMqk0YMSQ5PlA05cz2tSjooaMkWQc1QnQFwG5BwvBHiRnFMOaYZCcQCHgAmIpZDMZRcSmUvewpEKFLSCzCVxs/RGeC4xN+lcW8Mhc4ncjcylkbnkQqRz9UYM57JAQOn7MxeP8Ewu+3M0q0sE0ebw9OjsIaUvB9E0sZ0MQncIxxAfpRiY576T4QlUiaDnn8jHbu5oTw0ETsQ/0MjHhf9DZDYZH8vCT+Qjo1BAhFpCIvK/Ip8GIVGkBCRqkPItzRGssFbI+XwGk2IvS3EA2ps5kugKhmWYTrnn44GZSHGiRCMA5WYjDYZwjezJlBJMP0Nk8EAZTk1Eg2a8RMPHe4K6gREK9AM4iyWBFSK0D/ED1Y3djcgtIw1EDdELiG7ulmxaDnToIv3pCc2RfkV3Qrbi+j7W4ZrQU6YRktkgHjer+B2PdSb4Lvu5UEUhe5XV4uCAsC6H+0c7yxvZO385tIBDojIZxB3ctXXI1MyLId02PMHmcTWSvS/jxxUCypVVcyveCI7KVTvvfKvcCO+byQ+shLFJTIisgSXE6GBXJhu46dfxwjdX+X6DuKyPlkZoXoIusAnVxC+N2YYtgplSFXm18ITi1fhYu2WQUs2LlBjGPLLVAr0sA2a6QRvcAJNwy3SINZGl9eaYwkVNybzoMn+twA0kL0crlfMSvgIDXhhBEH8Noyty9gQJnbXrtBJclpEqoyjFuhEU6cg06FJRAGXtmIcPLoeolpFMWzcSNTkiHSRmEdzLTnlF4BIGehQqcFAOSsLO/UhwUibHaAvt07cN4/tuIZqHAPtAeCnRHCD0g5iGD0NHe1+Iv/eOojmy92ZAez/spELyeGu4ob+rVD+3ZklkW++gwKmWFSyA/n7c/AH+PWyIPHugoXgSB28P19wD/VG2A3PDg0TChQ+NCq8StL1S8VgP8jQZXIqi1p2hYL3PLYHojAil8+0XuT5yq7/Nc2KQ7EvgjnRhB3GguWYBl8G9mBSYisQ2uyRisCCN1mxaKBCDQH9JIhGR+g49UJB+FLk4sSKz+i0RvkHjQnMRI3nIWGTtnnD89rJE6qQYPpQm8SZsUaZYKzEW2FArUdw7RU+Egnt8IpTrAHKNrp9BWR5MFDCeXxt9ND7n0+eoWdjcNsSCicHiAlLeLFBC2M2XYAFla4I/85GBXhSPQvfuu+Mtl9FJox2hRv5O0SbTrXERlz3Eb6XZiF3CZ1tk16Ytzxbe/ZLmcJPWTcf0UWXn8kJ+XL+hjbl+Bd5wrRRdHTvhtczHpjDudS+wz/ceSY28M0j9oDnZ7PkzjF7su6UZy877uNWR8Tl5aYRzWD6+9LUgVlayDhqclX3BWDJYQTuiZKA6fuAjGeVFnPvT5j/u+rkbseWVLtg0Q7yhSCh9aNsH+7aSFBjaMQhd8d/LU3UQH7c+kXXlthXhX++5NG0e3AXJVGks6g6Jg2kQXkvhku1X16CLPDPXoHWXptfIbBDP4RZGzHPNxDqUW9EBXRdkv4VXvBAgjAb5tOHpzM4epeyj5MVyA3ZrOs6Fi8DvP4igj4eCCMqU0K5W+TZa8nj3td/XWSAcJvaQMtje5SpNojtVh17sIKJS00AfX1ouwihBnhiRu6I1Gduia/Icg9dU6ZoOWbPaozW1IYQ4KPB1e+vdNnjN5wtN7D7744jRvaRjiy5Jgf+NxusUQzfki6Z77TdtB7XIYlaKq3jeXnFzFInw8PXYxzFV+5ENKddSQcMSw2qLw5c5+EftCU/EntL9CcY8w8p4rxO4dPRpSgPuEu02zAJcS8+L6355HZ0Avr5kQmC8v+BktMjIM74Nrfd7a3tI3u4RtlKQ7shKSf4A49zraKD3uNF7QNIenDQtdHlTpg/EqhFZN1k9Ey27JYWA3neFr79EVJKLjQQlZHjY13Ed2tBSmKXd8o5Y3aUzlw26jzB4sqKWg1tMQFQ/ztK7L9Fd2Cxukq3MQm730OZ1Qv0Be3SJ0pCXNBufvDd8f2QixIVVowzaoJR2JPRLAFUpAqzHNxYqOyly6hmMi9Dp53bWoL+xl8wm/3yV7aGPzNQQVVbMEQoAywUhXuLeUJXqoBwFVoKGv4fsWr0Fy+dzX4qkhYx0pymx7rxp7ZGx5cX5IzyNmmPQO5IktRvzfUOeYjAhGcUl5N4Ly5KURydeE3O00hsuLd2FwV+ujvmkLaFdHDu8m37ejRnUna9qq8EovetyqzmWCv3KeAm4FOeTwhX2UW/noyqxOPZANM/L1bnwhhuUjJPkyPvPvRa+pKrGkChE9l8dDEhhvJ5bAY8kYIMyTrcm8LhdrnTylcExALWSdmS/AxiADnnOxTy0ontoLOqcX2IcitTzot9HDh6zwouYd0QNxvisbjC2qBF8cFHWRN9uK3b8bI3eqIyUYWROC5GefeMXioJRLHEkDqlhFs4ditFJWomDecugS+Inc0J1QBHRPsR0jJ2AZD0fOCNUb8/DJxCQnGhzrm9CKCRE2R8r460yIFzLCNesFAI6CSU3G0mBoRRJXji3OXcE1tBbO7Irmo60EQuy+n2kOWcVwkJN/alLOBQdQnlGAIUJaLbQd/o5M8T4OTDvgTvXCYo5DRyOLvDvFs2fiwkCuRjOoXqA8pPpgWCDvuDAF0CtzvnZg0mzs4a0grIHX6BLKp1PMzeDC/HaWYnnHHA2Z1UjBCNODM2PgUGcK42ag1LQ1zlv5/yUCsQ7ekUeTZofA+p8jgWy61UWzHOZcw3HzFCW4zhnPxxAIxHoqFH83Mxz6bgyjf/Sorlah+YAxb/S7IYQQAj1nXOAGeDmuVi0r+KvuX7S7OehzPMknmsb3ByZ5p5Q5yBM5hodJ+YzWWAsB35qeu+BZoK95pGDIJxzhgqNtGCkA7GC0cCD49cZmZJXAVC+RVMEyZxJlTmaSZWfqChwnKtGXqBSqfMfAwjXJq0CWsCbM6yXHCW8svkUzznHtpA0IOoFoDn5fKa6r3MIg35nKUnmnNrWh6cIuZzmu5qZG2i3IkKxyqwLOvXmQC4RHoPRc04z3/Wc1YSvIEwrz5yCI5s5p53BV+Z8EAzbZ3ZsFyt1RKYbZROXBd2eVqRzHlu/QCz3vCJLw7wX1cbN1+Cd01qW28xp+ZjtGSC0+xFaqPf+Jq+8UR+HHVnkUdCeB7XYQt7k3XlRnQMniBia0bBnJC0jJqGYQjlCz/1h5/2e5vz/NkbxbxNSje5Emid1ZBxT1nMNwxnnHOSAk/BWNtce5qgJ3BUB5WiVmSerghoqsfCAaKH4gNY3HJpzhTEKjE+hf5vnus98PyocjCxQAEzqFdxPTnH04K6g6MxzZXPeMougBRqknwrBcZ7Lw3PBo9G5VjT2AS6Zy5ir1dY8F+r0grG0ONdXJG+eC4VzHVAaUM31Sr640FOiyoukEEmV84ShmWctQwmDcNZpPAOxaFizLKDM6ytzrlpAb1jzG15A9yszED3ZvrgjOsBIzpzTPqF5bFqVzE2OxD6GZCEeKjRn2oDNnNsOkUjoNBcCbRQ9N9jjHDgI53HFTw7ch+9IHBVT8Jx5ruEcOKD5JIhEqX6WRolz3nPNzJnz8iLJeYtzzns+gwsmkvOmlTwNpSqwJiQoz4Q7DHCtANAXoJlDuumC3Kc1BYeuzZDqx1zuBYeCtjmYcYlS+TcqO77Sz/noEL2wM6gF0Oa8lBSXA7IjzWyp7Tnfno0iSsXoRKZkKBFE/wdc2JB9g/LoxxnJJB887wlVZpH5PPOblRmNq4rLrjd3ocMwyHmUh5vXqNc7I5Wj7UaXDfQV+5s15ZP/lFbTTn1Nkq5GgZPk01POAOouQbYK5WYUJ2O/cr1rrzVEBNw1da+HYo0CS0/d4B1KiPQa+NCsE9h2s805+Chdr097WkuLZ38piLjOlS9phRLPgSVM/uDMnbpyiB+Xupz2SV2gxzGBNGRzll06zCRhUYUBaB92F8grtsD6At4jxRiqUKCMnPvkPKNB7TOITsFPFn3OyO8Q9x354O73o00rK58ypvW34YHmEMu5IjWZNANqlK8O58fzGauYApE0Q1z1zGdKRiw9g/az75JaxG/EDPLUWpV1gJEnxBB3qg0htbXOHWmJmWdUIp2K4qlViUoqolMX8++0tuc6OFN7mvfpOfNeWpx7tx1Sc4R7I/VOSCeAsom4bE2z7Am1DwMUO5SS/ZSGT2cv6Wwj8sdAniLqR6u5A4KbcK6Wm+U1mnO1zgkVlECnUeQm5iy1BU6OdEJD0BETfYhovl1DuSTUjkPtUDzHpLLooApDLlEk5kCiYtcg5gdASGVw5Czxz1kAPotY0jqzM59FfmPKGJNDz6LNZ1lOGqqaxFt6Fnk+i3zCeVNHpnlnN++ph+9uOT5LQXNZg07rOgdJwGf5Apq/t2ke1IaCjc5ilvQsYjWf5Y3ZLgu3kHB5UdLuTavDeW/O6Ig/gXPQ2gan4VSv7M28XASku2vwsgDlsQWJ0l/NZ/Qo3BH9DWk/1wNg7IJJw7kdptlBehNt0YcrPaeA7bchufM7ws87Dj5Xau6I7bPXLs3JI9WDHhXqa048upxPc+1JVFEKfkioZ4FDZZ9WmZwa5/hV6oc98Ug1tqKzNcRzSrmB+wPEPEXYD4gOqVFKMDAmMuCIGT67WTJYBgZ1IFbHhJAJvWI7pCariT31Cc4Dn1M8URQ7qNQLt2WMc+3RQPhZ2S1KdaA/GZLZK++p6oMdwkpwYR3++7h39ovyuZ3SEjdirhbXw7BD/qelnQfDGdUXUVHjGT/t1ummV3s25cokdQEpoJhh2NNxEKU/HzdidR1A60HOScM5fakjSqPJHTkLdIzoLEA+hDRfeQA0pS03mL8pRay5rRA8JYs7JJtQvgua0b6TLJuYJ0TPBsOwDZQtA4mIpHpuY3Rcpr4+NtlCwVQCU2JEpom6WGuByAst6d/6xzqoN0RtjS/8n2u8FngObW1cedRk1L2w9imoTV7OEFFxY6quIqvviMzxBUdlJ68VV+oiUtMO/sqhJJ/j3fHVAEO8VZ92kfwLZUzESAcdrlWgrNSJ8fsoPI0dXE+Kr3dfxLvzQmjCMrTMW7+gf1XtM27uCo68v/05m6Zn3KplLTz60z1HDNaz01jiWaiDlt1k/+nujGtYm00C1TG2m0pXfIvENOnzBt/68JepwxTbqRWE6FUPxUOcHGSOUe7I6yqUboWgGWjpZlklgbCHv6Mab7pui2J7jRBC0xTaXzfGhzSrwyN34g6bM5YE/SugbAN8008Cj31sAKSNo5IadQ70V8QdhMgzn6a9eWA+eNJi8a3su9SjPVjLtqi21y1aZ/uzTnZIL3p45WI4t7aJ8Eryl31nXDEMQpGujMxV9+P662hITkjPsASU8P7a3COzH1BvyAJrqgzCBSKFLcOAzTOv0X7XXU8xtsW0229QquPZvm+zdBgsZJNy9YDSWQ+IbRS9vK6ez623PRFpJjHuoBS7c9qnJ/NRXrkeyQ62jdLAv5EgFssBPiu8aQU5GdUDQ/wa1kXJCcWNXFfXLzuCnY9rY28sPWrqGWfoyU1GwePuODqGobDzXHs7dkpzDw7aDVhD8+Ua5Q/V98NR9Oq4soq2qLcvRP+7QTB7bEXrdEgu5rFa3D4bYodkWVkwoD7sBB8ZTfUs6o9XgujvUc00EP3P+jrlTj009bscR1tQatTo6BvPzjYK+PN6wYj2NKDx0xFbYQ2gjqlCnhynSpEQrfTXxjrbel1tRIRBROXFKdvyPgly/drKXAotUP05Du75cEPvy7QdAUQLJJc+tDJThZQqb1g0nduh79WpfLzz0Lqiy8nHyEcWIj2tF4J9PUynlfwy22seIApd5q5N+s+g3TcJ+KPbPn8Zfx+T/+0mgBz8/dZCM/2ti185/f3GgfP3Dnzt77cF5L/fbIA++Z+9/Pu9eKsbua0B7R9v9ioDJV/sT5+Ss1zI/WmBsvWbl342vXeK7NHiet0mVxsZ9fXmsAynNSrvpM9XwVTZGU9rpZ8/bCQYHKzTx3F5Hw11iQy9VXnn9bQ37YelMl01HY+ozFHDEEhbhq56GF2R3Ii4G8Lclam1z296W91H7spGzPi+Hpb3dwojj7Dg+Q0yC+JK30fqOatIL5N8m7sDB+O0Meog23hIMyddEfTKNj88Y5q0X72b84420KKlrQfB+j7yTPlaD19p2BwznMbdrM6m3fbPXt655KzTz82U41PqlsxhPejK587r90915PKmE+pCWt1VpyR5lr+6XAXArpWRZ5nusJWXm+15Ge69nCvrkORggLuZe0IQFBEf3uU4Gfr1En7s2Dqw1pF/vN6nKy5QiP2zskKe0GyYo2kgfTcPnbmI8vFwd7ZoXC1NzpsxmYtlDKSb+OfWj+0Fx+VmU2p+wIRelK10vZycT7V+cm5OhmQTzeVACD29XufvwWP6bY5LcYrjsFZkbs5PdwH5R3e+m8K59xi/MS29oTfukd6rkV6u3aOV2MZy45LskhzPdxE0a1dzF09eTv1rsN3zKtKcI2juJETN77MSNZMo3AzGD2gOePtzlyhpX+7nfHb0exyHXictjmHGR8FJtTPy/cghUH7G3l/uW9U7T7/GH8Dpo3FevMoLAn+QZvh7liD+q12/+5681wsJeTGOyxe0fllrCAU3tm9CyRPNr8sguNcLJUzt5EQ0PDV2cx57YfXvpYvu7ty4pYacW377ZqbXt8792xbPw4rcAcjruf/ZGrMr6gNbQAejfrmxMIaEPa/uku19ScYVPHMaxquFiRCQft/tx5V8/F5u30kr0ysGu2ArPX46pO0L9H9waHhDqEAP96uTqOfVblaIKaEZYPE0ImQBqoYmgLy8ny3Kp3+Z7/NVVyJBWk8Xi7whpTM/kc77tXBxg7Je2kD9TMae8S+HGBergbxSZ4ohs/N5ne8XD45+FQOQs/aq76a/zgjm/L1UQ1QinoFXH7QOGKwV2gYCYtAc5UsRY0mURCM7OD2f3ZJzGfMrXMxzNUqyGO6Zelu2w2J/VF8ysRmrOaf1dYroidrlPTTlmIpmEW9YdeESSe3m+suJ6n05bs5Xn3WGHXOJuEpw8wyQSJ1yREqeCGWwo+Wy7X0tdKD77oK0yYVuIH3Qc6QOtVKzMkTq+hse0zbCpzorRAsH+6e3+YEuiRUe6BZTKUEKRcSPDFeqBTkEBmtUb82P12+zUS5gxX9MbqqC949ca3NuygHG47jnH1tOmdzrAUkQ8gaVNCnvCA81JpvmT3ITKsALyS7/HiE/ra+6/32SrUN7f2Ec7p6vMChXE97B9ApKg2ZnRP90Zi5t3uXHo+B2KvspVwMDefGWZvtNeSEr4+nNnM2otYQuT0GjovNax5b2BMlUNQcgYfTIfyKdm3xezZX4bb2Z/TK5Vj7ZOQoz5rsJ792iX1z6t3OVpy0QxSKtWL8OzPUJ2yNN5GctQbgVRlX6+JJjOegZFCmud5P6/hIiTofjeEF1wgEFh6IZKOpuplUuPNfHJESu9PN0XR9UAe0T7MCexH8GcNfAM8op/Lx8Zbgcz+b37Z4rdvGmcrmYMcHLVIBSyUt2zhbZ8w0QCbgBJPEHBDTzBKqcskFFN7prsKdxL/yddqJLA0BxqbkyaeZuMH5kJ0N55sF/CdX2sHxy75hK6Uanujro2XPN7t6TijrQpQ37QOCSmasL/GQRFdELzWuAEF7tg5ZL3XyXh5O7+U4Lem8PIynkoQoVdVlu30sk9GEJI7jvDltsUahpnyIHPpBcFnfd9CMkCoS7XtlblZXRe5RXdOmA9KZ/p1ezCevMagEIc225DvWztHINchZdqOkNcS6EkRw06SIULBVKvyYC7+nzxtVOVGJsvFkWPqnZyxtDLVHG9Q0yCdmsU9rfnFtKdgrme3Ahkq+ydcp0zYiDAv1E3N0iBndcgK/JkF2axburVmw5ftn02x3AQVLSFkZie+RH8UaHPc1gxGZq9feFcx7YpbXsoNIqx3VsGwk9eTZtqQNNHi+3l89ALxM0TkENM23aodafDeHkoA60QiCe1qgdBJq6evreGhC547nst5XVrRxSMZ5+cuEf2+zvMPoAr8dd9sVqJZlnRX+0WGc12ZoT3Se71LRBCnsJ1fEBqOR8/tPq1Slabm/GY4Rt0HCgFi1WbXy67JYeY+CBls97DVHUzDPq8QfpcDzzz50NkaQM179uln2425wfKD4D6mx3RO21/qZQn1BbNVSvut9V016N1B21ZZpTzSOfcGRwsiPT+xxQLntTqGdl+2BIV/pcFJbB2gwW/epTmk7JJwOMf9/DY3uA8IhavcHNZ3oG1uWzdFymxTLSwYVe55Xen53bi8oyzclAe/jEJYCr0U3B+wUcMWHD3SpJXsVCJFqs0axL7qCoXxFC3oSUl6GPOMvYTlk/1labpsqHrxif72mfHQwNU9Uq5gpHvvV38Fjpw3qzbWleCBowTvRmEnCKh5ODg+xfEex7+1ioBpPKsyGLGWhKYHylIH1FRgJxG152lg453j5hENDJDSvtVlnU0GVpl74mvVB2rDWgxV+UMlDS2uget914YIkll5ZzFjdVkDGV+sAsa6iNkKLQ5rL7a7KycN+G+J6xvba4HvidBo2xzELbCVCiTkHZmFSPKDfcuW6nLY4H/ohbMhaNBKS0Nd+L/fJE6SCf5gp1F+jCflNX4WITQ7rX/DupLOVLdKyaUbCb9BBjymf1fOMXOWRUeofnCXbjkXom+bDeZUTettjXtOVdKPI8BP2xOq9sG24k94m1fB+VAh/dBgfsF+VOzd5CAv0k/5moRNyIRMwoLzd3xL/DALHKAMQHnt1a58Vx2YbYmoM9zSQD6R3B9fs+l6dXtAO5gv0ga2d753CiNN3P/hYJrfJMgZxr5zUMGczzw2fQJMLSEAit1PXzM7S8BmNV4IlvtjidNveNQXAhKCJaIf+0/auDEqyy5h4jJ2/u6JfzfoX06Vp6yloUgXjhX5fKangSWemDR+spQ6odz3oRr4jci3mQQCMuI/lmMHrkZOR3XIDR4aDD0XURrYwJFbikxaZ1s0d9gLHRjvxdWRPu2SvzkF74dcJL14pxUS3jD6m3M9pZIl735/Ttp40htLsF8tUolyeXT1QDonYX3eQ3n9jQbSUb+5yX5UhQK5bWwIandCMvCei4zl9V8IoOWn2ELtXpcnKyBCrKgbcutuFTvdKn7ZMnN6I+Uw0x8fW8PxcIIaB7N160P2QVLnnJ3HwXDWUgPyRj2tXfHjQpEIvLhqm2OI7zCQ18elMDLm2T+HHZVfPo/jn5tkNpzylUfpUH8mUjkV8f3CoG7fuItLS477CszbU1fHqU772GCA3xWnRSIQNOSksk53xHtnowpj3J9NX9SYISKevrv7RMBleKm2MrXa+nyCv5aRJzMGQ9zgbLZ8j/DvO/Eml6yxl9yFH7LOrlHe4pNyoufBm+j3JajiygzHGO5bsyYczzU1UsA1Tps69v0/68o9eA68snogsyuEOrf1m9yjPTwcN5C6urJoTjmQTPBOa64Kgc0nMdnlcQO0pxbzIlCs2TOgZYQr6C46G0YsrssU12oKd7L9yuHNkjpPUAw5LlI1KLANuffEkzbI6mPtgL99jGgqHAeVHz5dndB9OJfAPhJw8UERl9RoZ+gf5GdlgjKwNmsWfNBp+5LUlPGBBdaImYgt1BRIxqWM5qXza7mPTipHUwEvq/8bmj8suhSqN0wXBs4nEnqogtSqoUJ8zRwTgyyJ6k3YOI4Hriz9NJIlbQENtMMsgmleD0XmeBD7H41Vlsa+Rb4D8lRYo5KBtsz7opetsI8ZVcFkPIj/2ro6/7MY9xVWU9ecZ7FeMYFPV+VsgLY0I76orWKosWo6rVUF8OUL8NcFR2DuZpYZcJxhVK0twnCT5XJBjDSPwS2o42x+Zh7CqTFBDBFE22cT1A/lv4ItY6ft47AvRQS6Cp0NpogCoNg/rc2ND7fzEggXI110WNCsbZRHsOcob7g1H9kmi4mIvmSilliECgF5Xcc4l1RMk5+rUmLcp9E/qGfdTEuEZYHgHxF2CKkZ2Jt31fFlMCY18CHOC2lYnM3eASiCR35qY8QDl5FUTKqvkOkQKKEN3UClSMKKDCkJykBg+FFjFgZJqjWo8kzveRcgNXoLXIH+g2SKCdG4qP+Hs3DCJT4AddyIZhKOh6n0OXnbvU3uWgQNkcworSClCsqE0BPpsbLcvVlGCZt2eZF1OjgfOZhfLGfCsx3979RSdJ/zdvkRyA9oBuH8cmE5Z468Gxi0gtqa4JK/pdBPF9QD6y/GxxBbXtIpNWex/UZ8WNvJaOUnp6DzG6yUhwkQZsWq0bjBCeAFUeFSJpN+qpY+BiDNbh0szL7e4O3ttfdWNH4LBsVKZ3HU9kNm2uQ9socZl0PvvIkb1jiPRoX1SfVa/sZoHrLsC9lPfJvhwQTn8fhcU+VRgshtakz7IQwZjNi0tkTcDKnrrl4OAYdtAbKmvyobEFSTiebdRP0zP9lFivgsHYQxrmr/UcZAWLRs1q0cqS2uJBkPWwbShVJORYis57MI/lC9JzkM5ZG3WnXU4kBoUc1p30+aCSN0AeDx9o9QpI/K3OdAUVRP1lDKUEWqZN+UFWInVkwp1krqMbAa6jX7nDAtqZM0YquUn7uRzZrpMLz93gQ5O/H2G8GSwJ3R60r2wAH2Y/ogaaN5J2lAJvNzyM9gQGizjLCLRjAw5pYXwVmZrwhtYWSKvl/nx859gyeijJ4G2+V3bRYZmsnxVKDXxEFnnETjLZ8Jr8pany5NHo6vJE3/B5r9AnGg6IWyPEOtQZA0GeqP0TawvGx7ZQh0jDXg4idHmnfPQcQv8thPlQ7lGdxxemmFugXp7ye36wpzXxHvb6TNdFBdtjtd6xDeLbmW1Pi8vSx9EDqurTb654OQoik1J4TTCChXqFymT8gc8MbHkFnWYzvAKx9h5esIXWZPnb9WsvAphdV7fEjvDK7klmw1sjn4JC77ODqnLdtD+Ewd7Zv9eEOxHrEEunjxr0sXyYcZjmas/U24gIwU/EL69vOO9LMQBwi8nF1qHBnPBKhnDQ3CwfPc+gPn3NMFmEuCuSX8R1MYKPdIi6NgVkMvL9MhhaOJNO/QPcc//809Io0gfFA6aOggYoUDC8yN7ibkwYjHyarUs8xfa+aFHOPjtAO5kDlX9cogwfw5MN4lMlv7W+KRtGNo5KoZfbM9Xl8n2djq9nLnLo+o+4RjVewvX5pmRZBJC/3ONy6MzYOPXl48vqC4GAvOjego9Wv1AFw+V9Vuj08JggmGJ5ZxOcvetrwQkf9VUzp7z98Oe9cYxorwW9sy9995v0nZ0QbDzkVtwY50grcG1kc90cVs/nQeEHlOra1t0lkEO7ZlcOjDUFecdK+xOqLic+6tSXq4h06oOXuYeA5D610wGGhB8DpaFHjk03Z/cBR69aBLde6FLGY1pbV5A/8G0R/npBSd/D3m5SZoNvD0pfk3Y6vD/PaFivI17NZc0u+LOl3308r5bq923L2UjRPCNnI7FVQKgZHhvtd1lFOiZXNNs2HCDH7XYrQHswOPJ5dRfXxzBZ4wzztEiu6nBeyZKDyldZVlwqsfkdQ3xNcn82V8snxjU4IQE12047hBzugRUjTbnEfNf6XSFvbLXMTCj3d4TJ95HaYh8lxRrNeXcL78AftNCbBjF11H3fi2MVrtYfOTVbk/vIC71EpbD3cZ8rS15KzRz9171wPw2xcY702Uj79TFLdm0nlsd15um42tDeH87yEoxfcWKnFxDrsXn2to/l0qGdc9Cz4cxGaexifdmX7Yg0wCnqmtH40Ef1Fc3eykp4QHVmSIoGxhz4VsF37kFxO1Jz/95sGJQuyy2qUozGccWoYe1dQv6NKIG/QWG0405+ovK41xZN/8WZKJDRO2DxLUUVyxon4aBdRfWYcOQ27n4PgdaIOk6e47oIrewIps0CwdcAtED4Y46Pxz2ZvuMS/8N59bWN6pFZF9v0qqfvVaRZV80JOPm9wR4Qu1LRIVlICaBk7bmty3d4ACf+ZuoQwiOPKy5Qdv3eL6tiMjyDa5QaYdS6LdotOYO0o1+6iN+22UcrW4jzOte+X+W+PKP9JxD3oU4LNo3qwF4pSrzmgztXvbpSEgT0306DeJ6glkELoefp/ZVOJGuFLJNzj2t2r2d7o4RW+bg+2AaNVIeAYgrziSJP59JNU/iG7IZvr1BycLtYYNbFiu19MgfGENCYDBF3Qz4ved7zJc200TYkVAkk8Z59UDoQ6VCG78tLFW9tb7AXzA6KFErJQP+gIlOzKUnEHM264ZQt1o1hCvAJXPN8PS4Tu5FQDeV7leJLC6BurLzuAIKKn3izLvoJ92/N11xNL6w5c6Gliv51Wd7UIBXRlZOE34ofvmielXWjmqTMvckL+8BHhiSvitdt41y/q5ER9xth368f2+k4WfpqANv0uYJoP+qQ/5fBnmbiKmPCHWE4foTuoxKXtSfYrBdl16gphehlfpRTse+n0EaSi+syDtj9SC7kugy5/h2NQWPPK89jx+XtO3D9tTHejFkesFCn7riANiCnO//pql7I0AYcslO+R/XCEvWiOgsPHNp1fSEVm+2Wc2+finMvlVWSsvDg0j4DMd/sl7Zs4xoKjNEayfXU7vrqGHSgq20ZdxG1xbGBijM2mlPThjMU+zw75Ex4X4nclEfZ5Xf4yBP0LGnYEArrGkKRbxidLBnKOz25ONd149d1zTy7jnv6z2pV8ObhZEYg9J31q+JRZr6VQNj2FxZaJKorv95Nka5shW4InPGxDBnWF1t128b23WM5pxwZGOjlTrXaQxsQfnEeaRB/Tv1Ap25Bnsenr8e9FozJ+JZRKcnRVFxy65qKtLg4xp1bSpJ40xBgf3z8vIjDNitRFswBzbwjfX8uUAkOrqFpyRf+dNlIknmTHlMgI93c0owwjHnQ6ugjJXHEe+vWKV9rRsQpCAtnjZQTqao4YtOLRhr0i03ZbivruXKhHKzSQigKIxyzcoe1v6ERcyxHKGNuCk33iFXk4Uy5PPiO1dWoeKevd2t5EzuOIyGfnCIrliP1lZycWrEd6u0qZFQshj6lYbWAzyOjDStkR1BbkPiQ1gZrK9rdMfe7caefsFQAh/TdQafAqhW4FWo1RUgMkrYwYkOpq+eTK77msTWvL1+7d8h+XCeA/bS8YkhYK6JukP1SBtaZeWyRv3D84yCj4ufycwxrn1+PYv69RMxifzQ7nAsz5s5Q7baxdOW0WN6l0l542XvEWCvHxfTVeG3z7rZJBDE/RsG+o/jTp/Xs2JWt/gFB1YExNSt8IVlmbuibibhIC3fD5dk1tm6koFwsSDzvPCjdhvAF6XY3+VUChvRhZ4VlADUVWHfjnr+v8rD8AANRdXNh0JRi/isTD8ZQZ9xsFB6+I4f94KcH3AUGjCdv3wp9zaiR2mC9B7FBX8oXEdT7KjzjFW9m1vtJqknyceg5+9ZrqwVJ/KmYPz8fN3SiWghx3xdUDp2bc2xowSKJ0X9s87w+iZ8XVPDMdXfjicMIVDMb3KWI0lf1qUHfIiymnVCEmpgi1vLIMSPkd7yEW+4DFi/mud6kR5p72JJ4E8QPj0u+3iYBr/Rpdw9zNx6KkyYax8nrzHtlM5N6k3QIHHs7wQwCk0q/ESGiqhTC1x9lmuKBjaumK5GTMLTYEa/29A3FkwZ3rqPeJPaJWnBVjXSIWPsljgwG08oCP7OWrHRc9fDsNz8PNRuazPZkyq0tnHI47xtGRcEa5xdySx2MAmNdmUH86zdW9oilPgN55WwuvT5AO4c6MFpRehdCNfl6tD9mFe7wE9/ZRHJdPtKKd861DJCazWk3HqvwE/rXl4fQK++ybnZx4/7JB69p6euKZJy4p3mWFsYbHB7gkppdLz29vZgwannQtWpbqO5jSHZCKSWxXrzyXAtEdqdNPU72Olxfhhoy3NqyPwlKUEm4bRA6ki1fGEVa4TPlIs1/c+5yMbB+pFw3O1DyTytdZ+BTNuJeNgUuioyLisI0M/MsfNfw9iE5fTk/pkf5LHLBm3mB8MjQBt3UkkSrmS4nZrJ6+DKQo/75JZl2uA6DgzP8pYPzkN222VS7KO7T2a5adDCmerrX1EHua53YhbJ5Dk4No+5VZOK0nvyZqN6dN84WGgPlqvpS1FvHws7RDpbxQ9xL/PciI0MnhEcQ3YarnuGoSDA5B9th/ea93pqMctd3fSlfjzGykkIIdGc85+wdbejx7MfrwpAggIOKuItxFMx2n+v1wy72I05Fcv0xiR83fWQhDXbRQh+9fSUDf1ghH7CtuDvXjuawt4Sgu8o3rBMI6Io0usuO8ttZjer4HPxscVwIvf9KIf3iT6p+vOby6no9/t6/OFMuqPPuc8+F7xi+k32RqdU+j3Jd+nXuLaa9HF7H6+Z9O3TkyuozJTnO3PSSGiSreyHUYw41LWy7fAuBPnonu34d7+QSGkv1iiYbo37x1RRdcqJJefJAfL+slfX5fJSr9hRv3+zGJvH2hsWcM8Mz/YaIlSakoCDuh5LrTbzDVxWAg1yfVt63UG/qaO2STBW9eLPJJoKF262oEE/EPyMA+Kcb4m9VGIayRL2xsTvh3PI52Wi16COPxAz2AVcy8r1JUbDy6F7dlb9CfgzRqI/24dUBZbt5TtPdQHzd5wa6//QcJyhobjFiM5VVo0M+bmIePaWTRc85HONYCUxHEu/64quOSFvvaAc175uf86gE0ZPR0NrmvLarrGK/3K5rvqvWOwcoZz59VVZh90av+jLbDwvjdtre0FcPtnJc07jXOqOb3ddRnlwj30Iu2WkaZKGsfcTqhOz4DguvwgOaZ8/20dtx4eGi6ZhHe+4e3+vlN9uEwPwy20vcG3fuiQ378riEwbxGaHT42afGQE28RM1WebNPt3A4Q+llxn1eUcj4zJWP+WBJ7yRv0Xjv3jflBzkWBSk5C09lfVw3YcDH2I7I7PLFNN6nvc/GS+G8rMR4wluVJg3Lh/hVV/fI0KZ9niK1G1dhZLwDH/DL7nvrv5Im2hcO2lc0+N6R7rhcc0etiDqeRRTtx3SxLoF0l1d1dLEL67KIpc1Vm/TnycGIIP6Siiy3lTII9D4ZYvjyuze03DO7sRWPwuW4O1Yi+x7JXWvZSLAA6I5XZ2JT8lvnGrQAnM59tnj3qhNGHce5JPshFxrqhWbX67XwxKeTofpKTMGh4HOfm+xm66Vu7Q/geVrxfGEXzWhBHbiM6bfBvNtKfk2+ECM279EG32bIQWAn9dbkX0PUjVZF9mPSm7fg8t36bPjcFcPnfEGfELe7AAbhl7GVhseiM8msgUyafvl2n9vxsnorZw1y3vLBpDHf22c0pXOR7Igv5izFxfotd2Zb2lJ05ZgcmjjwdLzY/WaBkDhhZ+9T0p+V74jVBtvVPpaWRgPXlx7ja72Slq6xNE+UOnk8W/evVvmdvLuHhmxpO0+KKu6BjYQIHEpJ5TDUuT0+RNBNihMG3TehJzfo3SGUVSxc5R4XN6UcnCH2BfuQFkSWX/6A9fUCsZvoiWfEM2e5c9B39CzstcXPiX5FiX23t22D4QLUl4qDFD+kW020lllB8rLpUiGaoX6ywAlPsifu0YyB4Y+m9r35X8jmEfc6xQ+agbmwSmLq0xGVVD68c/LZVQheiP44d7480e8gfdpBk/abWkR9V9xik41dsIHWac8uGLJTmXcaEhwcmc7ktkjGXLTLa3HoUAR4iP3BZFnjRarq3UDDk5qPwBuJhClJ1yLShQtxwftSAzavwxMiN3pw+Ai6WbTnTnoTOfFOUC51YYo3d3bzNSG2W7qVZ4jIniw5AjfVC9Hc7VTqSfpC2oE2ptQG5PKmRl+1FaYBebNsK+6w5T4GD+wDOqmr3478YbF572RgdxQdxA/j1Qy3Jrpum95o8dd8ZHQg/P4LIoqFclTQPlDqm2P3q04aVG4ZC/dXod/2AGOmcYF7KrtRJO8dw+GR6xteQvoeAhbuHvnzjhM+ZukkvuDs1S3zvXg4Z18k9B5cmsTnVgmUKzQ7iIyyJfFyUl+ADirIKWFE+ftto1+Fj0C+R/pzPqZmBeX4GrUR+SfOhcb6U66MerUDEGNxJaS5yeB4O3XIvcMcpUp38ttdbU0FOZPnMa3dRoVWhN10i3PgTaWoBJw44HpbemtJXBSPZXCCLMJVa0XKpPF+XHyVUWfDUhRS49waJLOGjCrFRmmaH7QJHSnd8S1oxMg7CpvJly/4E+De7YUyMJ/FYrHZE+f9SeL0uS28TZC3Ffryq/quLDivyXLbLUpRGueoOFXsg4fORl3K2WeHKxzgPaPB17cQb+mDh7QCuXcZtONqIRx2hli7C/WmHx0yfkPPJeygThUQHzXA/25uTVAHW/SJ1k+8EYRbibJerILzw2Az3jDVBXnJKnfxizTzY/fY63AFV2SPytbsc5uvNdKNfCB2CFUznrdlJbBA1a/eEh0j3tm5yJ2hV2SVSMlH21mqpNfl8+NzUNB4b/KvdipQel5g28Mg+suPd+qGr7Nw/vJugnwgOqJ/7ZcfOQsX2k+mO0Ef5SIawfOju2BJo6F/7U4yjiuEiX5HzVtW+tPoofYe0sbdD6bMyOL4Wq3uRp6acPUWfx9JFO60N//Ib3e5Wa31z7uyEXeCD7+CFEBW51bwSXy1ely/MoUYexkP8HwknQMh0OF8twijAXTvHPrsVQztvK84fEi+NirP+5K8x80Y/AHbBgsoYFpArGHlzldYK+e22WG5RjEBvSynbxhjUL7C2F5v0KKVzitGQf5nUPzP5UBiR/otw01zW1d2AbpHZdav7rC5QLeYN7z6uTZrqD5BfmpjgtXVntDW+x7QoVdvSNsJ0ta5dbzZp9AahPmtXCXc1ewHdMMElQyJzRdBoIQY4ygoTNzHSnxB/urhayAshjv32GmnPkjI90h4GmciDTrsXuBuf9fusAFRhozWJZ6g3zjEKOBUk3j/wp7Tn70UqkncbqxXnWSIwfaNBrRmovpciLq7bluMqYFEfa7xhCr7RIhQkqo0vxK7eKo1X2oWslHU1T4PkAu4IGYM1MIVOkhMzxt5V+CBvEtna3p3tvGdey8rmeAL4l5BDqUqAdWp6IPKAIq0gFJ1CrLAC+8w/P0S1EDGSjGJYdSxzYOOjTEaz4HyUPy9/xW08os6D7dp4RZSdqoM/XVu79sTrp3lNJ6DaKsrM89YEGuve7eiGquVM/Ay7igjGMvZxFtmMorrQ5K0/c0Cjpzddfaridr14UhfHd843n2xFMK5zXU2iCth6Y+oU4d1i6zoJ+u01L/x9YKNjhgRayJ/320IphcMfAcgCF/F8z74JmDs7Fx8c7E6PPV12BR2e8izEp7fzPn90v8qXxIZfk3S/T7ow9tMxOnV+VpusK6U1V74kMZCYPUj7viffOw9HFHoo+e2usOYT8tOUJvZx83O2S9GSgvi0aW4yRamFZytna0/kEVwchlcuXHTcU8o7WEe3/klksJKemDPnOvVuowli0V+X9Ph6Hqz1CzeahfM7senCEsH4xVH2WzDp3p4LfVGGuTrWy7H5XljJa8E63Ss04hRSyrkwwv5FuXl64wyi8BefaAeXTF+EKXw9WgmAXc8zXvXv4Kze/Y6wiBqf0L+L/zeJ6a2dmb//WbnK6acQXo0Bu/L7U5iYkNzAuNK55put4WzsTiT0REHtBux4l/j9uqSOm6FZQ4GOWbeOYu+fQzBzX5eWaNcu1w12ZD3a7k4JzCUQDnIm+kbZ9I59jUIRh7sL+ecQMLGq40wGTLYjj1vvvdYA90V/s7F5JCv9MfSKSuY5UPHgKRHcYPLl0FuPE+kF24wOCKtAmaAr7VlD01g+o2W36gpvOIhnvaTh+TakbbUQnm3gBGf2RdHG4njQADh15NX7P1EAMifo/+OAJYa4Lwdb3c79OMze7+26DNlYFXxZne8Lvq2xBewZRCJM62cK7BqdyUsIK0AV9rqB0UVoV/C16E2rYgt3kSk27sCZznUXpOOr4Lxgw8xEupQKN0AenP0zVh5I5MQ6fn+luJqZ5hWbb9cSJqtsYbxFfIJQFGvaoAv/oKB+BE38mR0Q3S30h7fNMxg5DPaUnI3bpd+ROq6rkhX2Q4uAHdQ9CUHtrY1pps4yBJBUS8lJtXTJBfafQcanX2rjNYu3G/NW7ZgahgTCe/R9Mtkh3tGz/RUT/MTGVZtvqA3zMRnCqMYVKreKQ/7/b5t2wx/P+DYEL6dL13ZiOY2H0KAAs0uUxJh2oHyWiAKqJGDrb0l9h5zA+YJehtHbO9H9qugu69aqQIZWzVDgrELsiLi7nljKmTogwAlOj/u5jSgK2Yr9WQmZ9LxvJvot9rQPhZb8wAgztCZtdpKJsTp4gLjHzcy6eqX2g92A4yTgIc6DWrQmvZx6FB2eDfbAvBEmbm9guyHocJoTM6vUbkvm5ZR/pxt+zObCrYtY4a58P5BKQsnw7LfKOuzqEY5ilAsA8a0V60XMS9KYiW2LyYf/HL1DQwgFd5u0tohJbzYROT7X09l4COdj5vhxCq5CA6nwQrD3RpHitv2Tr4AKl2j3Aov450/FTsZiFGLft8uexhkQg+JqtDUGiysS5Rvg970JeA0C2gX18Zl2hvfol6jdR8/6+Z8ZCCvGUojdAxiXoIx7ff51p/XLujaR7QW6lGEvtMM3hl0Czbz9Jcgw15vXZF7RzvzvAU3k9c/nyrKYxA6ui6to4PcetD4IzP0e0YjdNbomyWR9nk81NFwwoueSTiPSr3b41DoSaAEKlxrob0VFv33Fq2aWzKLqsY4Tl5Qj9sEUyYW56V7vToFtIElNJbucecLFqOGJK9VZwjRiL7IUEtY+2Fp3tDO8hb6bwFwKrIUly/VESWrwa7eOJ/UPeDT1LqsQ9SQowwvc8JarIa5npzUQB0af29oKpD8hZuz9qiOryEwz6TrazeQKCKXoZiZ3k6AaMYS4Tm/u8w96ceT/N6uSJdKSeZ1w1vJSaUsZcCwqzEdIawvS/OjoOhaHo45CVjXb9H0VRPLjXr8Zr56OpLbOUGNKW2KGQQIdmHf/LyJ+pFfrUpIMOFGUO6x3eXNjkNUx98xI7MK9VaS9vUZmVfUH5LKKCRF34hA34VbUwv9WCit7AubnLVgHN9Iz36ckse5LYQdss/Szw6u5qCY2g5GJdoR8VofTchrC/Fazhfzmq9+lxtRb8KdWJ86zuOpJ5DqTr6XIoFxzxfBQoIYmv8a5NuR4r0sjv83e++x7SwSrAu+kgxIaJh472VgJiGEcHIIIfT0nZGZ7Ko69/Ss1x31HtSq/W8gXWTEF77dFlpOta6RaZO8m5qBQvflFicXmP/YiU6bvfF+QKF5CPymPEnODp6quU/83e9Vx/z8vC6Dv135sPllfVcbZr2WAqk4tRjzS3a5D9pISJUU0HHCeGW0X/Sn6h0XLV3L/oAk160pz5fAcwB7Ih0gpOBl8Ldb184w1tHQHkZtA4HWL9Joj6ZaxTSYNQLEJvItRg+ZFyttHMkPd2kDP8MKJbkAvolRYJz2khFjYNng+yokcAo6sph2yPenJ+Zua3wCNxviebjSMbqK8oLIoZXRDUtdtP4XhT/gRTL6aMQHe0Mb99FwP4j2ViqLncfN30hhFXpXpEk5zL7eUhScVdSDOjp1UNffrBxmRPbvKkk1H9dVE9OiSRvKk83BMbJbjTkCxmyXA8QouWcJ7OuqQzffqR7ARbkyuCOzXHpDY1A7BUblDNek7uO6f17By6fimyWoitLSlSOTyhy55riMd15tDBFSPzyQqTsPQtPyRNP4nB42ZvUGMkzgwvbb0W83oKjIobkFhrnxsGDhgMdtgwSDu+E6ZlTPNcQJcQ575SEpA9YnFVEJW06obo8DBlHExqIniFbKjZin4h6E5guOWx5OWFbeyJwkNie75e73WXfowBKVRQbkhJxuxXJYQui2I4aawlZXZHxHco9lobkLdeSFXfa9w7WGEw6pdOBtKyikwNGxdICI90ZFD3tQwjtUcCzpjoGfVpQ1z1ZBb5Ry5SL34GcZJEbHXlx+f8WYiy8sjWYyVA2z8JkkJYmDRfp0F04x8WalmH8Pyl3CiP5FdfHrzdxWKCQ1YCXzbwVJxtEViNyuk9cqia89l+zO6DRyKcnH022+hSyTCi75TZt7fJABEYj0RgOxqeiqiwXcJtBBk2grRpciAYxpIHZK8rCSttcC6hQI8rxargbCY5XmSrTK2qC5/oHI2dNznFmLaxIlIGV1Sas669cs2tpPQHpPsIAJzheJT1qlN3JobVXDgHs1oHBIfPh9v3xuMTvdM15d0BXKch8ENdRvR26lLeQL2Of2RWeeI5LLKNHGFArC2IQ958tYfC0x7nuSEr1bmadrE8cMA1/t9SoUpB+/9R0jULIsafujNdOK69iZlotBONGhqBMNheJ5TegFC3L2JUzLDeKCIcFI/1wtf1/ta3WR6qwfjLTQRBXhYASausf8FXk/8fuNtr87tUBLjPpUiwNrh2VqMiibXTJuxeuBxqAGyGLW0d0pUuTroTof9pjjrLV7g5LDGuLGMXfmKdqWrTcv4bvXiBhC5AZQiiEuE1KDWgw+962ialyDr9FQrdXPoQRu3Vjzgo8fhIvJ6X++U91MjAUHm2Q6SYLGMIJQzuq7LmvSgDnM0DzP5ZbWe2a28vGV46VbG3AWNXirc8PbiWLcHxCpUDVSziNt1zNDkvpEMQeoDBbNqSdbhG0l35Gw5lHzrm6ZyysQpoklzEeJlefX2P70Lv33s2N/qpXtg0jX3DoW54Jy3bFltfnsn7rH58EhEZ8cnvlArOwWs+MEbwwsouq0+G3vGNlivEjJHaKRfyyr2D/NYScySXxJ0TXFCOJMez5sqT1Lqk6w7PcjARGuPGYDAS+TzX9rH1VRDDu4MZkieq1wXBM7ot59WU7w4g31jHyxG7YDnb5UHih1y+mrxFq8hv/ZQPKeKjlozWy+d+17g1WFeFUFckGzlyjKkpi+gNDC+SSukFhYN4QQk4xmJi0MpidZ+jHJH4CayV0U18c9GcItWZFZrPdjWA+loMCfuS+21qYm1Y1QdqX4Avnv4Yy18gx9fZqJITGPA7rvMBIVd5Bjg7Ji591oNiHDi0heQygfEkdxu8oeu+rt3EjMrtiw99HmpcF8OFSeWVXS7sd2/dAE0fbYj6c3fmcQlSmrIGkjyov6jGvuqvSBOYGZzlQi5dUmP6irJkoay0Hsl2akDOJg4z22zNKfkzv6aCg2R72/NqAP0Ita/HfkryLzvojBqwa+EVqYTotSpV4opKfXv6o5ZlxvA8PC+0PuSsvotlAXbxPyNYyTZ5JtLuNpv59rrBM5KPRo7Xl8VjT3KzDoWYEnBwOQ+r9jyd1YNhBf5szoPUPOdD5yB25eIXnEgYycy+1eNJiLwtsqtCZgtXosVPsv2jH4zHQYzLnKKDD+/s7QU7PCNLlV+DvmNt4polnmHWK+Hzh28YolyHf3f+wG+ESMyyMCaW3Io8Zya/s5kdSoMuc24dwi/8+35JhRlXyaqAJ/x+p4WldiMVmJpx9T2tO7I+G7E7B/xNh1QTnC1kp/+Mpvo+J/rmv6wVonk9nw/8cdlpdSedtA7IIYG/90iJYW2UhPNWenOv0UNkOi048hzZhd5G8+b/T/4TuGeP78tdCWtgwP4ru/56cPBEr9f3dO/+s7YuFxFNU3zGoAP5Yyu6FktyI1HC2skP2/nEd6sC3JOjz/zlv506HbcJhoTC5e3+Z/dgdP/7p0W7uUVYfvJh6Jf+6/hFoiBjv521Nie5HFyv5Snvr953n0rmSwxZHMUJ5ZEeCnfCvebGYQMJCIzMep7Y6YiK4/8C4bfMeqUQzSjNrs8XOHfx/f34Yqr1LUCEcsTGY3HJT1MYlNLKskb7W9S9JnS2o+BWb9N28tymDe+jRvFtNUBOCzV0k0dXuTORK5unZYD0632sEecUaZ+QWJQ+7+6Mjdkf1Mu2pHAJ/BZB+bY6hcrU1U/IfDjTCvhJ5V1V8dWukEHZhFvcn62RE0r1mxjgOSeCkWu2mNFp/t8HnqzhuP+z95Nl4Pt+ka5zFvMD/r0i+mOCX/DxdVSHSlc9W63ReiDLIy/d9kT0FkD40gxCpc+CfPiItINxT8tdseY0Kyach51Iza+iYItiegaqwjTzno5wlzeD/5uJ2hQVaziHKubmRV0xbGJW4hZ2fx1I9HRRIPPVnxXR0nvLdqMDy6gLdFLocrtewgn9k4sNhb/JLOaqzhbD5BJkr6WdoyDsx0SBsfRo3JVoT8e3+sg21DcCiWV3ZNsVz3qvOWN96kkdezpLnX/5L5Lw7fBkO8SYu0tIllV/oHaz6OqLiRoCE8tiIuadR6cmWdXRZnzDFtSQWxaWCtZ25NqIVVInfLw5bv8NzcFGNbE3xHTxgWS1tmGR1fUVSkCv/B5+GljHt0El0fxmlvit8vGAHI4o3EbKB7O40fYrmvi+RG3cr2vlTvRB52TB6q2q9rv9nOMn8hwZf9bLhtL0ob/m/4Mju69YNYKnJzVZzr9RMqPlksfibwbwRXdljtUCuDRpXArK40+koMPIWh5+cMZuVlWJ76yjtwRopoVIdmlN31L8aXZSbgv3NqTeRiLuf/aAEz+eoVGOfpC+Z5BK+PxTQt4Kez/3xny0MhTyGce48dIr5Rg1K+GX5srJmAtXRM8J11r7o5aGrO07jNC7M3XsPPOn3OJKqtyOuVFBWZNPFugWly5xvGxJk5KJj6sst2jlYmrYFRT3r9MMc8qr0QDechQgJ1wW2xEsnQTYGYzUI3v1LQ1/1ioawa/L2FofjWBfWgOQ6KMunahSoao6Nh2a+fQgw6peOE6KeOO7O7pPSkhJXgzaWYeJf2JLbUjH1mrdHNmRUFUSYiW3LPj2KvHLSraOzWU4MFpjsroKHvDEXx4Q/aDyMXIxIU5Qg0ojCplMeYA3k3hs2kBWe2oqwmzHZFu0DWzvmIrvYdaMaX/UKHyBRDjCmikQ3Q/aRDABV4L7NhHRul0TeRJL7J/cefYVYBIcJs8AA+VJmT+JmSyjLVXWDSfxbLEuvTv2NyeuxRIHMV1nGUW0nQmDHxCBnsTG1ZQ9ynNaIN+OaQ6NEID1SUJ6wndtbD9GJS5OmizNaxmoXZUVEm/dXUKEqsje7CCVXRXRftFa9rUM8z5SErexKpjATKN8TRSYK6nHEYmKIfeq829Crv6HzU3x7egfUO6kUwkMosIZiSCnqy5ZeESFoW2Ky41+GYXKSrqD4nGyjMhHpZib1IC79pj5fnj2I6eMKZWgC3zKpjnhSuyXdHyZThUGRfLKMZO/0DPX1RmF+t4Kp6F3yGRqmVzMMb/x+2qx+mufpVJZXIQUWRIoiONINWAZMiobiLLzzDCCgOi7FaCsbNfb60STzIZLW0v/fvmNpQo0JvXtdFHSrp2aqdKGYppcxuGvw23fwohha/NIYL1JLLrve6vVRE8TQkdbIk5qaEQm7IUGvnjSiFk+2IepixLnp5DFW9wpxeFv1Xd9D6L8mEvU3rqmHHb6MTzxdQpXe20szH/o/j8Br1EtemczXBqoulsS/NGsNUbrQOvjGyLmf3tYi1A9tMQR6C9dM8nkR7vIHcN6Utm08uf62g9Pov2ovEZ3V13cl6NtmyvZ3uvYzvFcvQfNdYhU4cGSjuFGazBdMqXusP6k7+FAdT7eW6b8dIu3w0D9aniNFkBfaOC+UkcQsIkrzrPN76qoWaAngTGdVKkfZ5t3qofaKH7GzBKmvsXvg57Upzuv/sqe+HhVIT33pFPv/F3aQGxXJK5TA6WPqbY/jb1etYWUFj1pUdbYt7SrFnPq3SPgKj2ROL/flxJaUvD8nD91ebYnmkkYIji+rDGuR4aoJ7vXHc1kQKmnvZyuj4GovvD9FXHYZ+vXiJ/24SD7r0OkeKKFUEIcp1yu7YErhnZUWYqqy3481K42qbF9EjFk9r+k60rPFj9vtxXbUBKqwXNzO07fe1/sb0RDN2omDXvynVE2jMEz/eevyJA5s9x2aPNfgz9U1J+ob5W30T6D9V9pj+XdGi8mtgXsTngEevwTAZiLusnM338buAGIB7ITErJNbZajOGKnX1HHPT3TbMrLu6JPWH3JXNLIfnHb7fc2W7jt1sxLN9+u1QPFc5pnCeiN2Rybk7MqlFtKnNunkA5uffmTvTr8oFc+lHQ+Oo/vxvX+AHn+cVQzxVRjJEPW/adbNyjfXttxkgDREjYVTc57fb9crZT1tFsRL3QZjMuqf0skPxXSSPTXj9Yamx3cvaFyLDuTPmSZJE82HEcf9egB8Sg0H71vC3e2lm3/S2ievjLtSECxiIZV6815c7RD2LBT9Q+fBtwdLvtiHUSlgGT3RYabDDiZvRBThpZQWYjF+P1R4Dd80oaRZi/FEtqTg9Hs/ezEEuF7pCPymWJmTh1ZvhMWzl93e7fUAZ+Ntm5ix21BohxBTGaSbmRWhM+LUiJpJs9Y9Z4+Fn/B/x5gUi0wG4tbWFnPX34VEqg3z3l8QnZ6H57qA9SBSaXlCOaPxsfDO13xMzBf3ZQz06UxZeQyKeWYHCnmmU1ayJ1VNqnMywHIbNLm4HEV2EWZN4ErU/GyOlDkfGGGVXcB7caOL4O/IYaVbioNd7yHdIJr7tpDeyW3iDAqztyLP5s1Zk9ytDfS7nv980XPVw7YumyqQEU3lo70+Xtf900oLFJHB/kTf8QcGD+VAZNF8WpAVH73RjyjmyCQSlQ8KUWFyZrPoKXbf5tqjHEn9WLPqgcq4RvTfKl53R7RFEzmC0L+gZo6140SmSJeQUjTSVAw8x7dNcjdVzaqzxPok9pu5O7fsAo5KI3l6NapRi8cEcz0FWHxQ2Wr9qbdxdqc4jix7T9Co+PQuVs8sw6Ejkc1ZoqUYywdnexCzeyXB3u6vkNEmPsES2P5VWhlVHAsHwOAzbfPuFLBo/Bc59Ia0Phlinl7EgwSST15FbH7eFqXBQ99YsHHQyd68aynSRcSSDaZFOC3XlF06U/qRALDwB8sWrcVc0Gvc8PspbRyLevh2rxe5Ex+fr6mZPR4GYJgWftdj95oMUtgGZ22AXlD8a4+XJS84I47hlN48PhjJuk/rvjKme6ID32PiZz7XZxwuoIzU/Rnh+S3m3yzDcJ4QrUolQub9ns+SXUYVKAT+/gef97Wu1PO55pTDDLrhcoUaXMiIWCQRptt72Aon/xnNMawfZKOS/xuAwG/m3o9wtEe+OEbYnZ55d0V7aQJ51fLDuouSPnNp0cHuP7Pa2hkQy2vRHWIT+qUyuW/Rylk95+ArvyoDiCSUlTINprI/O+SD89RrP+iBAJ4X3t+k5X85kn1oQRo9RAMxY2hbg8wxHNZAE6Hogzm651emGL4c7bp8VJFrxbzbVzh6UTSUsE93czfPxLiN7NPzxW1TGQHz0ZkftG4n6EjDf+7aP87wfBcyWv+f9Kf8BpyjSDUGjGuYbBDtpt+84k7WVuo8cLG2xPrbHt1G6dVuNxI8xnU66KIJb+ov383dC8JyvY362gGwNzpE0sauoW7b+spjD/PMN65cIMUFy9fJXWLPZQVSQM5DTtlhcVmxsLlJl7kOoKNLdMUUBv49s44ZGv97vSfFX8y+azdlUztK+pedlP+I/LecnUn3QwDRnRWymMfVmtBLkTula1G0eVgZPH9aXVTFolyb5MeqVH05Cv2yCU1RIu683vp5n0DoOv50v1Hct7J/9M3k2xNXhujQuRxbFGJOuuW7i4qqecsk4IZ9/bpJMOvFh8VVBiIH23ErMn+bdxmhma+tpn6VNJu4vHdXuRaceabzaWZrViRfnze2wptEnz9UtyrBmZoT9/H3+QA1kTZKYnbE9fjBZXLtGs1sVA25nmd99tOGeQ1byOZbmeKZXxp3i9wPPPX5nhwwKG+H18ok79MSaZBbMHiE+BOEtPToULwLRacr82yblCyRe+p/nkEmSokbBwKDdgQ5TUvt9+EDQFqeTzH52wpx0ryHNHesheL6P4w+6IIel7gQz//JMSJ8R2pNBLtvnA0O/T2Mkv/MC36IUKzuOqP9y0TodT9fuTxpr9e/q3jxjRfy/KNqG4o4YjcRvkBwee2voU6wCHa0FWCkgRXKbSK/LeqRCkkYpo9pLc1Qequa2zPB1GSApyz/rdv0jbWqiLy2BTM1RImbaw63faVIZF6S+QpqIhagdH2odD0m7cYQ1uVlUfx0e3FaJvfZCqh0tApQoP8grd4WXA1hGrEn01pPZUznTU0XlZ9ygCXWLwjLuWv5i/ppDUdx4yF1+0X7NzIIvdVD3Ba5fKEnRqX5av1OPn6icALPzsN2XU56QSy1J9mpWG5KZ7z779XbAuvqxe7mfn2mcLUdSfCwbuUVA+vAAZNuyNaww4de3vtHE0u761TLNRIw8aeUVbgwh9rxUaUXiH6tnAqDNw7RXnlv78d2KmiWNp2JTKsbx8p8Idn0DAlXR093c4/F3DY2P5cv9OrY8Fj/lriBaB0R0s+y+wy/cZdCsow6Kul1p8/T9c1Is7aWu5Q5RsNolhkGxjS8up+yI3xYaET3GTTbcMQ7ZY054i7arbWLYfft8Dd1AciNgRWwcH4/Tbb/jYwjit3np252CVtYeqx7CzdvlUrl70NL4bjblBryzoU2ejr/LVlgcSe9mma9ukP7w+zwzUjw14TRyK5i9XpAWhyjk68QyvR1Elnqoli8fyJLYYKZj9m355BqaKfvpaAxKYW2eT2hFI96Ka7u48Rc/Fiwkvw/zHsP40mlaYouE02A4DMkXB85i1+/Ph65Ag7R+2w+osaEkptmP/3C2f73x1h+rdX03zXb4jPtdCs2r3xGWqTbU8foon/Ejfp0ZpS+9Yzp9GcIe9ApEd/oYZajzVCbBpVGMEWJdU4wBlM9PFPl6Ji/b1DEF6grwdC/F7+3XXBLkq8eQBW/Iim8G2g+Pcmq5cHY/GbKJwjecpFNIwzffXK4/C91mJKONo5HqeEmtF67retXar8u4fmwgz6OFjP9DG2GUUqhQy9GhmTHVlBmjeC84hGIf1VKUarUjZVL7sjj/M1ewvgiaBnxbUEtqr5RHUpinLTr+XG/Pp/fnGqCX6qZv4Wxemkyqy/AAnXwI//TQl9mIuMbdx2fxbAWmXTvuvIk0PGs3Lc/7OXcPYmE8CCQqPJdZRqLJl0agC6sY9Y/AGsQWariG6ePVrWQpXNIb7AAP16nGKRezXerP5rMHOggrK38vIL/pt96v7NuQGrOKqythQbqVTR4zvXh++Ej5/O6P1RLD00H03+FDtN/uWQuSlbzUTMgtKKRsyrwx4Yxi3bqJ/k9dMHzHGXWfDIF5WV7ynbPfyHSnWM9f2XfSjFT7UpcJliZYkYF6/1iKHwp9PhYgN7kpklYvx0d532fhNvr0p2XXrvw0kIt8nlQjFlCcKPQHwyGr9f4yGVeq6MsE866258v7iJkWPqNvUUSftutp/e3NlVUH+UGJ0Zp0BElIkdXZtYgKW8CIM8lhl5p731otT+ld/pvXfp66cx7yD7/4Cn3QIT0vcvPWvrDAikkkCJJyFsuMmiXaKa+1q2EV34ShPEPfHCDLcax2hFoPncGyps4/NQSaGsUiSjzLMTw15AlOkIfJEvtxQwl9ij6ZlRgjWtJVyTHXfJQWLZP1SVjOZLka885+Y5Jc25poFsl9KLiz3jIuGU3SZMDoHoS0WkWYXTqGpYRLx6XZdlu6rZ6sHjB7DX+eJmIV25hdufpVE5z/YjFTyOvGZkD5N8QztyKYuUlPRyxO7HT26H0wctWs8D0In0c4efyc8qArlAabrfDluhm6lbN44RttGKBhnRyDlLsjgPmtRjNTX1Nu6OJVWBLAjAVE/Ub+fOc9Md4OlCgQRrw2+OK8q5nk2Z8iTNmbV/c1HXmAvPxuXWg0o/tOsaEt7l+1LUGxmnk+7xcphtEbqNR2adrl/KdJVy9PIloNc8X0rEHan029jRBpmPY+72ZOYXJPTEcQhxrb5j18DanFiyTzUdQoB7G+Z6w/L6NstSu0jHeSRMGXlUibPuFCU/4dKDWhBXfMfR2K4jyp/Hm+X+XhbL6bGPHrxuyazo0GLfEvl9NudeuH+NEPM+E328XqkeWtorqn60MhrK/8ztp9mmJ9AqmDttphKXGEk32e6MmejKke1Ht260NRvJWLYYwPz/FdI++HjkXM5xymqWjbE27N6oM7rDZZMk8PBhSP2RbiJY2WWSo0hVV4M7wrx/BUBObH+WY0duP0eHDCxV9Rjhw+sJbDf+omtIfkJSx2uW7miexinpLGSyvFuqCb2yrwPYfwVk2KWSd4kniIDwfrQe/XwxM7SWv0w9rr6jF8SINcbOCAREUneA1ChWg1RPUdzvsZvnb7wa7OZvr83PhBTcAck+/2p4dd6+/noD45iJnHnGPfM8Tw7oYG3xMe88FN39y1QXxuz4f+yBtCEmTWauls51TmnRSGkt5H61G2eWhJu/K0w4dAmsx6SNoEUEt4zDcWnr97OAoarGj/G11S/8g9bkltMl94oP1n+Xm2FugTt8/vTOSt6bwXzWEvbk6P5zOz6yPJtj0oIUUQc0iYW4zJNur09AO1419FssEaokQ0RDv7FvfQFVB2JKdvslL9Lv4onH5Tru/49DGnW32KeECgtZUHvMw2117iYoclqq1RD3fLbHALC0+xX8U3CVM8AI/neNns0VcACQD3n+gRJ7DzQQUOK9FOzvigHRd+p/gLQZ2XJqBZw9HntalO5yNmcBXamHFzqZJBvvinp92nECl7EUTtAxlfz+G6ofX99O1E8a8MH9N9ufIea0OsPz9e9UEZSTAVQjXRuPBozcM1V9KKMx7UBsT3tg6tITsBDapm3krW8ndY1OMvqjJHXP7AQ4ISi6GOc5vOnzk45aNO3OfS/dR69lJWNtdhecqgq65B6lfjUdqGWPTPYIPpu1oFWXL7rqxb14qxuABExI1Nt7DSmNZILRqVePDl5Hnex83pbG4wMHsdbqCuZGIrKcux82YV3rFjgPdxAdYnj92Ow3Q7HvOTv3Ewt7Hmqz3maJv8le6hw4L3WXhOM68k8boQWO66e87IMVrogDXjTXyz7qL22wW8yWOuddsWBDoXK6KLcBbvbJ6YRnkH4ioU9KE3RMS6N74ym570M4lAih+A3s81CtLmHBxGtz4GHXovo8vwJbVd7GXBagZtAFWlwZAmKp7LEGkbc37FqrmA9Y95okHa52IeqywDflawDN/PSVtFen7GSPbcie8DxvxQdGqH+uqx4XZp/8yHw6oWoZAg1PsBHStGoHPIq+Htk5CrjZM+c3l3QLfT08I7g+9I/hzOK6bFng/zlbwpRdSuzyWthnrJDdZzYA7dAeZ17TSJB/OOV8J7WXjubBepeZVcLrm7234pugVYzCwRxXme+ofP6ir6kTYE3gD7JBD/Qq3VyvkZgIL1LzoSN9yC0lH+zDFL2UcbDPhO6+FNdNSFuzO0hfEcSTvQ6OBkhvJ51TsiUeTVqFD0kT9bchNnNaB9RTE/sRum1u9RvjDaRIuNOC+aHsvSo3KcSd2lfd65ppjmnl1pvZlD83RfzmpbuBwfHax1/w60Tk5BGzrgQzSEOsfQQv3ovXGkLUCW7XTKu/fp7TzWytVeraJDds7KWhUcUqZ57dTCulDkw3J+nDs7nrhA5WCKodmoj/Q9nM1X6yTosGz7/a/oQU/8dctCA57z6p+YCbWMQtpjTjPt7D3+x3UsRxHwos9rdvDTTOrRF+yCcBvwfMRrqWCphqhmK2ZfutKA9O98akqFlPPs1gLCfxBtVbhBfWC4DUE37jCFivK/ThqeYP6gQnGeq0u6rKHCTqKOjTYL8F6Le3h3f1sdOWGN6bNYyjG+3S9WhUtiUYwaYIpD9yrekSMNl/oxvm+vWn9gkXVJvz3yG0sOy+3ld2YWVoSCBWL1LaGohCdUpaLKrqr2RR++5zZKZazg47VEyXpbOCoPFb3pniHnym5VsTaOu8t4FdEvCtbPV+ad3d35Z6MjHNTLXOtItLRVuTaPwAKJJ15mUZ3IcGOiqVZna9BQGjoG5/OCPytmRbyJUHB5+9svFiB0pdG00u/XTx3xrtWx5AH42cuaIkt7vYhINSZFGVndzMA3FfeOtRwp8wVbXhkoipwyoqetSV8WaXmylU09eMURT+DnL+dtNPte0fC+csq1I1XSpioinTyG8tGf11oUnq4ibw2XGX9zl03PSbypHB35iT9OKjIHzCKXuPBx8C+cTbM+hY7p+Ys31sIWyDUkJbsYNakBabg0T8qRi+awXYPoM/dSWM6fy0gere22Mw/+/DMDx7SIMZv9FGT8pfUqeb5aKmFHZprSjP14AjYR69JdvC/zw+kqYY3GWAi3ZlV76xWH1+hvd8XG5ea7WDvQqmwKjS/W0BbjgM0tD6nMqn0p4OO1PZzkY8cf3FnTcig/GlrspO72u/9QeYJnzlZcncz+x9vCowidX71fBXkYSbcqgnSS440HNuRzT4wbnLlcP+pbRzfXpucrasBN09So8Pqj9cfmoz7a6L7s/oSnfPZ8fQZVxETteXrO8ep/bPX2tHrzsOs9Z3W9zobzkVeGBhCD/tucLxgz+d8SPU9vfP2NlpQb1NyS7fu90Xfrmwc732DlFDDqq06hSvbSGAaol6iAQbDt/e9a14caLVM/EUfuRvoGy0eK3o1vBB0yTOlsIahFwtUaF/SpJNUHQdOfwcl/cE1+3BqCASWNof4y1dHEimYtyMiyFjmyFx4KqiEeNqb99lzjPhc117/dnvmnyDf/uouSU7O7eF/vVvKHnFohX7L03Kt2c3/25lEuTgvvcoZMDb3MeO2sFlCpi4ti+2fKCRl90pye5m7ukdYju+JWKMlQapl9W9xW0T7mfMM/b0F4G+eZMWszUljzvqBZaoNXQr8RJ51ZpLxpPStf3w7v9EP8QccUXfd55VCaB75/eibJLUYOy0RO/HCPePvUm3fVdzby7ukbpvLxy5fV/FYr+ez4+j4ZLkV+WYNk7tjab9G/KrZGP1Of3dGRpP1g/er8zq4nb1f90s3+JuTe1egwjoh2/ClDPqxVZTZpR2ygpvctPzpKp6TLPVajufm9BlrYZF5X3bjc6exrJgPxHj4YRfWM6i0WLZE0ttlDw23fUl+IB3N6LZTNzxrOEPUBYQynH9CPXlnzPZIv7fyZPH4ataJbjLe16WG3Wf2e/anQT1l66JNneOV1qIWYLVLz0J1/c0FeXDSwBhLfH3owL89flV9uvGBdIP+heHeSR6wEgZ8zqqznjz/7hXAGg9zGV8OFfFo/LAFA0KEAHUtqp6o/UfR6nR/r+hXaqAKoPzfXN7gBVeR5u+QwerqFKVnnjn64eqBPjD74THszEf9tw05s2+qbpZahw/0GHct9Y7E5V+8fzwv725C7ot9shKXqaCuzIZWsGfp5/PHbDrrfbUZt4ezvvpALtzrLL5/HZZltsn5xIbGuWtUIng4sdoeCUwGViLdfWs/Xql1WiXnRLYDn8QES++E5HsDhVo3hJxlj/gMX8QNGmPi30CEDfplAYqYaFgLq3Hm9iFvQ+UjesNP+Udxlv9610RJ/M+yL9jbLow059Ub5xmrWwUX8vAV/s0TOpRbO8o/jgHFBBRLJESvnK/dXvX6TwkH/fNeUbnjv9TpETVH0q1GrL2250uvxY8Kb65z4rU/97TICQ9UqU8he/vo35iPtNKQuSYf0Xiwvjw/mz9QTL4tzZtsJiGfdsHP3sTNIlftbuBHK2uPqezXo3ua0epUU+TKWatAogJVvp373xEcTGJszYJiadLhFQHY8jTH36vnuIGX8Durui6t1tMR3Dq6q/m1ZT5ELVPp2unoNnqaVpemmA5jhaxM7nLnhNZC6BDvdn7/TOAL1WwOMCj45iGMIiC9A5qwvVUY47+3VG5INjOL1sCxoJ5UPRWcY+z+1bsE59Xx2W8yBMokJAjwmNsXMwi2h0cgCmuO5DK0QvG+Q7bKKTuf3geDLQmMaLCf3x2dTAKzbbkGbWXCSbh6xpNjMyZPozaz6AxJmu5NqZE8F6Bl/6rv9nrasl8OCZZsiA/pMma8mgp4G9qVpzumMKJF4BERj2pBeCtwYLOEsO005PFcf6oySfGHj61X4pdXbaug9sr2Ot31IKmm4vQjRGDqhAUeuetb9Ld6ImDQWUQuZQxrmFakZE17kiQ8W7VyU6fPZK+uxhEgp6xdiHQ+KrC639Fsx82whh/hHqjEGK4h7QhLtUpVz1r+tU8jZkK5IY1IDjxFIo3tgPlt67muddbfKwXdF5D/Ybo30sntfNIJQtMvoEmklCUtdizS45XpZbmasrohuUM3eQcdG368J5l6MKXeEPw71P1avwel5Ro0vkCaX1EzrIpsDTRUR9V0jtbgwRJLUBGU5DvRG2WxmtZcmRLubdyyTw03vbpII2wFTvUUk6888HUJHp09NeQbHDvN8tQaldws7LGHpXt1nxFuf+DNWUQbJu9bHAvYOWfP4/JrTIRDol/rxVhELyJPmTJy2WEsyutaE70MRuePlL77EwVCFdSHTNiE+7TaSRgjTPb3UkfLFhTQcpv46RJv3rd/0JdPefrcUe2vSjz4moXV8dbPSwYeO1U9FIeffsIoTg5jMdgftkoppBb2OrMP1I9IMrdOkky4sazx98tg2neyJ9UVHuR+A+keW58Sx2r4nU7drgi2gIw386cP0f7kbvPsIXcz9GK9GOYmM3hZfprsusEZr1w4hPDyCfmUZ1KLMOOBgQK1rJ0uUQixWLFoicVgMmCbpFbkBp7fZtSntgDNVjZOGRABqk+Sz6dZzzIs+Ic0b2DPtc4Z8Ml6Bp0FnPi9ZFtgGqg1CakBIPfCBM70j0Xc++2m18/rvnQG/44r29M52ekeZ3vkbp53eSWEcF/29k/02IJr8a0jr5JZf4k2e1+1ih89Sk17244kx2XbqJN3NVZpvUPANWJj0k0R3GWkDy+cQe8l28sjVIDx8kdE5IL887omGmNQYD202zqZeLu5iIaPweiV27sGRuKlbFfDyy27URCQnqkRpUpNEmXX7SsAbVBhzLh0CQscg5MMHAZ1ieZ/HftrMWg84em6qF4gWINcbyTKrOYSvDJbSkW2WwOccsM3QkXlGj5pd7RSOO0Dklu80ciUh0sNJpYY1/BWF9ksD61JDssjFFeb2L6u1jiPtbgVcmkqGRImZT78j8ewpovMGH607zTucnz3vMxYi8sUd1qZybUbvUW8XNItGIv3junG/pzRumjNDuP6L90rcdLMNuNn4NtnuMq7wxkNPjuxKzx0N+5KnN9MoSQeLdWXX9/COBiU8g/SzCmJ5dBqOSj9HM7H0G9t++PL3IcG3U/9MO4I/PO2JUd/BWs8reHUaKi9t+0xKhVyUChxVNuvqFj4Xb2PNk1vaGxmE+0xf85q/b1HUOgMLN3zgbF5Ip+Fpdz0WTZUYNomY7Lfj+IC5icYy2qf/IAd3oj0puVsQp9C0qyPX4r85y+3eIXnjmjROnEyC3iIVsZB37VXDFBrY8xfWVnqCV5TxwDKCAhJNsF2BgNV4fPZ7LOklNrsFm10hhSbpTnjgY7zjCShI8UK9RBQhJazPJSCZHqyqdUnkkiL6y2NwCs48XYMUm5+Rxob5eFe5ipws/KE4jTyjXlmoGFoZoMAR9OO+8DJYpe8oAPhK+hz21LqFllNGoAvyl1pb8G/rjSnXZ33PaySjG118Y0ay4i6qG6t7OIV6CYMs7OPprGyulEoRguqhAbVp+qKcnhckPPa83ZQddO6Exiyp3CtYZyeJgI68nHJdK+gHclitMNcIYb+vnyUGTXta8QYD4um5mPQN+Zl7LK0KNOciHXqo7iKaey4xj7mEzBGs/5WJ5V8O32vfY63N9DwiHTuRMNmoBOkLtefrfHytt5g/c8Yhd+e7r0moXx50MFVRU5BeevPTvt2nhu0GEkqXD0O4dyFdLYps1iHchjgzLE+FZfHUBpH7mF39AJmi0Kq3HyOid8CRbOgxHenZWagTF+9zaD9sh94CCZ0PiPhhkFqeuna9nM0Xmq/im+SX1nUMb09ATyWZmfpnHynW8biTA1G4HvOB0J6xHBguGrDWZmFui1/jbzPhSnrKil1pMHpRjsAHct6YRVgjMGVizwhU3iLUp43WlAWX7SGa4Jx+VT8FEQ9FdhqttmnyNDLf9V8f3x0oR+Ks7uvv+ECBVG1Wq0f5ok1oBjmZaP8E/TP3Kca3UIn89LAzioZpZ2f7JEQDea5oeSyLgI7HNUa8R3yyT3Wo8/2MyhhtPJU8ZTC/B3TQXNcV5u6OVvjMpr+l9F6o0449BIia26Wm3jZigQFrs1891vLnQXYFIwfW9XxhtxidXOIqAj1qh+lD7JXreBof5c0hPLVQZz/W1Xp1X/1OHFjn5NkcWdLy9TY218PsRWSsi3QWcYRe68KTAmnDB7Rb80xh9TfBHxJwJiansabeEpvuRuCyKqMIvwU9nv1Y6jEG5SuqZV321OKC/Chf8eA7dg+rI4+WNfU6bMx6aqlt4IOQBjcRTLNeYgLtWctrhUUBors19gEPto+thqzxbLG95ZhtBBnery84nlgkNUx3SclldQqePYtyqiuLR8KyDfrt3vBMZP2JZwK15ReYEe5GvaO7JylT9izpHp6n1jLC51tvwNoNcSzwJ0n7sexbYskE+YolOFaiMOMG40XQj6f6Re8XSiaucSfcRVsFeQTcYJDnl8erlhk3kP4s1o78gH6vN+jyATLOVOq8fgzz3QljXUoup4lu3swi75trW2zl47LM03xIiCDXporPmjwjccNxbmzUYYFUd2zyf85yMFlsNGbunI1Rh3RSnA0NO39Me2z6ZY9lV9hxvwWdZD2tqyYRSrCu4OUGWLIcKa9ZKvSsY2PGzlp8gIew5ocGUF2BtFX0y9Fhem7y3coiD734MD3cvJ0iF4bKRX6mbF4167EeTPPtiJf8Pf8ttucnxqDVeuO5pMY/rfYwJbf7pQ7nHoWpeXjWYmBsBMyzsZjcv2llGOky1Zy4KG76Xl0edh33z0BHcrNo+kHiG+p/Km5Mx0b2qnq5n42p5RCJ+70q6CW6Z2r7nviVL76nfPlqjY6hydEVocU6WmZiVipk+MEQphx4IdiRuOrWSQoFa1z57L7sf8R2RdsDcrrCcvilPIn3xkEK+G3hIfVnIRpJV4isMitSx+p1fpz64BmKGNb7QsRpLBP/k7D+hh0m0DpAwmy7j3KwJc/pDf6wCpugq2rbQBIw/6Lviq/0710Fv4ulfUApdzD+eGlXK0o9ePikEP29ldgzYpKEyHTG2oPj+CE21x+zsyK3LOeYZdnOzT8WkvPDzwnLQMY3lNx7ZJ9kpocXIskLuC6Ni6nIiSw6HCmLg7VGZpMwEBxIawR5+q3jL1bST43tgF0E8CDde49jc5ZngUokBbyS/+SVHUQhWm4WNXhcV1xNV1JsJgrQny2mgPisZiX4ZcHLCJ6hhu3ghmU8AoZqDmC554kPBsnveZ1z5lqlPAf5L2ZZkApegPs87xW9LcVO3kO/xIfdoTyi+nWg/1GKo5yLeUfCzUUzVNRt+Df7WqsxPtpcA3FhlC/T6FvrygoroFFmMW34Tu9IV2vXXLtiUx7KZXzQV393R/yrpQ0NOzu3k+zcx4jQA/d5t8w+38X8vAgsrZnPNs2CQ5at3dLRziUj8gbxW5xugbIrTu0vkbzBjYWd2lqDGmhzFMeP/q7MtED5YMo18cmIORI2vb9+NmW0FEKpiVNvdbkc8fCqwviMqom/0yUZ541ea19HzF3rticNasCzrdDyKL5xY/z7HrvuQphf9fqUKuiNwenp867m1FIDtTJZJXh034+maJRGCQr5d/nhudOVyo00NCn3U2PU1GXhNJvqc1l+l35KTqPwDZPV03h27zrKQ/tlp+onDoL7z6AdG67Hn8wdiUxT0GYqh9LsX8d1Ve3x0P7wPqSBDRv/dZeL7+JTkUescuoIi47uqzfdVHxY/a8Ivxhd478d9HbVEhURTV8FW/PyterHuPQrMzQkZekEq8stnrtkQfJl2kvBktp1wpf6NiDxuda7DBC0wwxj4iLQM4sSATf1Zy608ztM9Q952sVPAzT7hnjCYDGuAlrp+jHQ7njIvZqz5UnvzhzeFauMLERcTRm9GSWr36SJn/1cr311vMj5lxT2onJnSCKmZIQlaF+vLk2KsshyUyf6inxvGCIqXB7m9SMrtsSnu1QO/o2bydReszJZBYhEFd3FeuH1YzWakpm3wX27XnyybyIcvkQCYynPemEjY3weT29+aezD11vSsttolcY7+XKbv2f307PKt824w8L31lLkYiAbbi7JF0gp7GL1WGlrl9WtepRMGmHY3i4WoTAvgTbV4PboLjSXvPBYZ0Zk2tu3u4Sz0NMTL77j4qxuylPvVl9GwYpkTeeeavpL65cne6HUG29jv6B5mni4x6SsGvizr9MpSoNprJTDwE6iK3M6JwsZ+4VeC/lJf68SCbwJdXYm3qDHBYXkXdEwGLXf8eQWMDmnLx/fz+WztlKrMuZciRXC97vizyRrEc/RnOa4s9wHRK14XDl6/rBJjfGmxOeNXqXHcenvuFSi7dTnE41u7X27zo/fqjQ/HyEKeQ/ff8PW90ruv6sZfUZi2ALJWffd6XpLOldq2TMGP6n46LLXLKLdJi9KRzEpBKjbDcRcjRDEtQzX/LeN77M8N3r3IRyycEUStOYdvTVoxrGz1rGcupyM+7rEcA+8JJvzXVgf1kNu744/a+XQzmfywuDoRhVI7dwaRqnuFc93l1PkYsgYP4QyqV57I/fma4jnm7+fWkjXn0x9fJFyXKjbpv/yRqjkG8f8vi/cYvXiwIp6WIi0jk9Y0l52yJCVk7HH24TV90UxBuXAX98fw9lbm1mslp+z/vWabyXTGZoKwzuBcTy+q946R0Ganc6p8Fn7lWDulva5f62eXu9+ck1XDp6TPp1Th8Eied+opno70ivElCosIQgdrqpw1rPwfDjdurOyF8DEbaaf5Dp7+n1cLq+bC9DWe/2a5/biuFvqdD6Wyf27MBGmzM++UoJ//8tfR96/n8XEn6cft47+aglRqkj//3f+770DDrGM8IrGOj/6ZaP8JL88b4MMzvxjz7/qt7+0q700b+cj5IqIYMgvvrOPflt4+C5Jm/lSCdWDctD4fTf0MdiILNSBVvWbk7a0pKM7Jt52nSZhFhohGEeWXLVcmaXvGZ54JkJEi2eyvr2QFrqlXi02YAZScoDq1dPxRZc81Si0EmBtN1gfro+A9JbackH4txM/gH87TQ3m9DBdyTx/vd0PAYUcPcSnndIVsusBKpyWB1pHQIxbACWCx0FZOtRVa9EkVXqs30y6zbYCyoGx3kq1OtaFgHk9urHKSFr7StYld72En6svRYaIDOlxg81rnd2A+nzWziu9Dufgt5GNklXINLCo/SQro/rpwmmQc5MDti9rl88q/f4KkdfhE4dy3vIbjth1LqyLrfPSTEMSSq5fKEsOqxywc/4rTb/rWDTl06Xp3imw7Y1GfKR1QM5ci8rjqbdbP1miV2YaeLjCXl3yPPleAon/7VOS2RhPo7yl2XLXKMRpU7zHowEcS74t83Z7PmJwu7isZvi4P6RQWuA+qapgNK9Fz9XGhwde63bbEBC4Ddu+EoYKlIJ5PVd0C7+7+Hwg2CCw2bsI3uXnofrVvubb+bQ7EeSE4H5/v+qH9Qsi4k7p8efyayjHg1yjYRUxktnyeFMu6usKoYWrK3flTovOAqvT5jD8qvUgjRq8vpE3AsmjZZKltioXQ4BXqZe71bbnyqT8lXIRw7wD9+C7H99wOUnY7LV5Ky02LYnvYfdIigBzNKH/zQelC8k75tJ3fd/wOTnbgGc+J54V157dv4RZ1qtVFMxJjPL8fV4ffjv831d6BsuXSL7Hz5bJAa/l1ogY5H8ih1SZF4cTDa21ZmUAwQUgDCu7G8EbLVsXY5WG8/oEmrS6fqsv50uFrMm4Qv3U2nUNHXxACB4sTDh4uuMNDOjt9pCAh+Z02SWfo3z+DkAKm1dH7jsmVUV9bbTdgtw6TXoJxGNRbS+kbjD4dSOD1Z+YzVX1NcfKJ0RNHetv4f7KHHL+JFEMapLVOdN3B64t9iGxgzt3eVS7BathbRALoCI+yf0z8UnlwdI3BbJzEYvzfRYRcKzBYDpsH4qEz9Xyg1BMhUkSxg+0FX3ACyWFWBadiOi2JnoxmySv4KniW+yL1IxRx6wjzYuMAVlC9BOPvzHe5NE12PvJGC0LsWBjKEJAxlDRk43RkDEctEfT76SmkxhYNamvsVNmiPwujdNYLr6cYqCy9b1ZD/gtomOHoYvI7xYQu0bryu76YFYuf+f3+ZtgvvdR1y4poWrszu4ixFxc702iAVnttqLRw/vRCMG3PMxPhwuGh3M+9IcbNUyL25jt9jogO1HzO47QiEf+vpVpxVNkDYSXROYdUXsd19L4DvFDuKFSWojqRDnF9RK6DnWJAinBSivBI4eaVXpTBHLvHOlOj8P7asyr9zGAP2oYq5GiQnIyYZlz6eFBpGI3UBTuGbRzCPKTCg+COHDKkD+UTGPHj5BvoRv71r1h3wK7uAblSYsooF/TDGaF9O8N+ZrY0a+J5Z5pevgR/DWrzOgqZfIKrBLKM+FXngZHS8JnAe1TnYijBJ7soaipj1h8TN88GPgbd5nN2PwboyVjOH9jRAbzNx3pGOM0xptxb6jmAGN8ER3DUOb0m0ppwxgYeE6/nxT2TXKMZpmwMcZpjDeCMUQNDhiRqvXFPwE8F9HUsW4jB/5AaZUAPmJpaXg1hLhot50v56f4PKtgI7kbnZAksG7cRbzAsux2BmsXfLGguH2LWB1SDy5DjTrJoTRhwmUgXgIMBPC9kr8ymn4nnE5Ee0Kx8lKiHpHipNDTtEwHS0vpjqdAFr9i2l1hG3jxGloVBZncXeLp7lilDLuFgS6lbe9Oj9CQVpJkiIWHtSvYFEvsA1rs6l6P+NtOmTOaN6hmxG6RIi4Yn0Eu6w6NAQRI1pF+CJxFTM+2EzInHtE5FVZGD8SQdDK2U5vkFUM6TNpX/FUx35Aqyk8wX/xSnoUk4QpkIsK0ELkbV+ZvWBDK0u/0VOBuJKxSMLkJyKOnTu/GVHaUI187l0f2NbYrWHM3CA1xDf0aXhpTPuFtYAYQf2Uwi9+1jJgWlBRkdmxnmLsIDSZmvYEYTFzekZLJ/6BBEbUd48BYmxcpRUgO3mP8yDZiFsx4QsTbrwhEJNIx8Dzu0xj441hWTWPUImu/bUIJ/QCllvjoN5iB8nPtq23pIRmiIjFvYYPw1CNR3A96vdvGgnbVPjEiscPGlVk7IlMBjBZNJ5xN+6gaLX4ZD8RO2BzYCaMMTjjBLJJ8QBJ1Sl34EQ4fOjjv6B01WjbGSMYQV9MY0TSGTsfw/8YwpjEiGIND24kMmdaPFVmgVB8+QigkcBilbiUTbx8WcpSy7wXjmpZZF8odPfFkyZ60BeUPhXjH5KDdC3bbniatKYZvlWmIFjIY/yvMu8nmdASJYcEiiCWCZhIgFzhAg5pi0ughGyeh1hd0GfLwgQHjAesn6izBR/k8vkGPzYXlARRZavUT2U3QvmuF9AZ22+XSi0xPTBWMBQ1KST7iJkvrZrNSYZYiu3+YqpmVUuRuEooKTFp0s8U8ZrZ1Qf7CDlWKQu/fWuGYR00cYbFYuiBmL79M9vAznKh+v7L7x92YJSGBSDa8pfLCYDdwZdD6MsgTieg41AGdtQtsc7KMXgZ8ejIE11HTvc5s+IEvwG1SrkzmJuhLqcuQr0AJVrmncxMnG6ZY5MAWlTtbp6xN+r5mCIXynGSxhFYBsyA6yQ3f5PvfGPpjGuMBYzjTGMoki/EYdxhjmMaIJitMZGSFUorhNAbGx8xKaWC5HhrTGIbOJJshzoB6zJL5VJw/eV9cgJWayjTGOI0xwhgtpgs2xmUaw4cx4nsZTGNspjEEGGNTRnQM6V9j4HMsxIxxHmkbDpQmtogXTQdVIrOp8ZNOboa6gtrBSOjpWiY7XUvMiros9gWVWYV+Z0wU7C142yca+sfWjB8xjbtaTHNcMcmK5RvGRsZEW5jrIVa1dqbUtTRjU5KWk71PkizxNNG7jbVZGhmC7C08P0zPg/J+VqgZwa5u7r0Uju6jP11/aG4+Fx63MJ5Yz3C37L6638pg8SfVWrkqj+sWK792KZduKectpRyxmmbhKxnGhtBjm43OeDWyxy1ezh9qLPTZtDNJE6FoZW4p3zBEWWLsb2HiE65Kg/Es5DPehOQSf8tOphMuZI55BeR7iSV4bjIeZKDr9K2V2RXKB7affstgeA7fGAvP62kyufZ3s7D8e1/nit3k9iLdh4FbMz0XyykWMTuoVR5mENTdvk5rxevcenfWULsNKR2h4Qql9kkMDeaG8GD4exm+JRxPXcVxsuDuF8uT++oxU8UPyt+/IWJm6RlUfHqpwjFZif4s0LEyC6S0ZiNJgcL4PLLqIFTscqLYh8IiyIhWpgUamsao2RgS0WRiZUDSRTofRxL9/GdtLFwMplu1BhgZzmdHUl1bZzq/1q4cZKH1UJTJnuFeJI6Ti8aQcynyyhSJdvHd0+iuyEU+9MNdzpuRCu1HSzXbAVPZoQ/l0Bx7rAC/7px09pdOB/4PY/Y1BGSvzeNP5waw+RZ+nLAKnq2F55yDMbZVdbdG0trb/4gl04rNkGitouLul7cimBf+L1WG3rvYfHpFY4yWD+G7u3/S2ZVoE1EyOrRfQjucMEjy5VQdqgHrzvaCc0jvDjSjN1SOznuI4c7CUDU2KIpQ+iPA+tiLd4gHKLbb5ekX+WXMO5j/z17d6vbIhGA8GIo7s/LPUzh0o0jHpXsmGnvXXUAH2BETd17ujYcX+3LXgVbqcETDP+UkycO16yOYMwxpdokDqXBXvPP4lD0yTomODiHhUqjX2LbPX/E5whgnAZ1xbVzrB9TNs8afLOKTvZ9oHT9DV8kpFk74fle9qQ0SlhtF/RAEffyKUBkeLA7zo7041l9CX86HVkH36rU7WGh7KkZw90Y8un6QQ6tzXjoadV9kaZfwP6FFMVppo2muLj5y2hGjx6oWBPHp7LojxNr9WaTE5r7oOWH++GDBUF6wSHv4gwOpPqZjgR3lEM3bRiCRToHL+u6I7aiKSinhp7/QJpJvQJcwIbJvfUpIJRCD4Z9Cwczstj0o9piIjtyIpuLlGwH226o5SFny8oFUCKbcSrbB9v4q7dIqVcz9UqRgwLW5fXwbcMPDi5bzvb1YUmpiWphsjNIRKjq5sxqZRll6cQBVI2aEiVuX+qhk+eub/47EB+gbzIh3dagtScdKO5Zc3BXCxFVX0PCVLNR4c1zvE3zQMqzZZ3y23d+O67Io7c7ZLQLpgW/2OJfhRGQA5M4jKyECzETX/1hAEwjmjDGqO2Jp+4bJ9Smgzp29TE/7RA87MsrlbxTwpgblpdqahYenQ0yzLz7D646NYTtXdLn/kPrLTE8Y1So+91konFwTaaGoqLeDdpo5CAMeu+XK5MUff9LKBelqKFRiF8YwP12ylVFyJfecP/B7Mn5vsTgN5L3TZ7XZdz+vIqqueKEWKtmBITt8d2yvEw53LKJLUTeo9Upi8gDD1EQZzXvh9MIvF2sABhkDBhbTou5f6aj5hP3cZoNsKL002j9vuQdNQh3PoROaYKlxDOIJHxlqt5RffG7xSpM3rNRWzNvCO3Vkxr8Xr3x1C8Au8VAHMrM/ocMxFB83fNaWi6TRTK7OejFsZFBwHOil/Y2f0kdOwCaKOiYHpfd+ttxp3UsAV1D6vaM9Btvxrc/t2kFSKq1b7sp95i/nd5+R/okx0aWhq9Huvfmt9yi5vLFixSuzyMuXHASImlvD9LuEK24psdupzN61TWznLJAzT9zCGzBoyTZgtc+wCEzC02wmG9r7kixn7ua+oz4JRaBdRkT9kAbONgzldP2tebiIJNPRkxzRqU5zYcnnGkdijTbMUrdTqg7t0DY4BsDLiTgL3D3jCtv4MqAtfzon4igfwl5xMARmOIE55zUDizc7w4eUhiae7WPOI2MbE3npoyVD3GDJ5TK8SCdYJQMGaRSFFJ5gsJpV3h6rC5q71euMmxUasxno5Zb1scb/l6GyUc+Khzd9ZAgDugVErIrTA6NwsVJFXvdsA+vhNN6Zc1j0GeY8PYquWrbVamd4Fhqi2PHM/IyJdi4VTixTcaN4DweVDEMmGsO1ftL6eIjwlooPGROjyLAkYrY8TyySa4QwXLqE8uOHJkxpoSXVTLHmOeNES0lH2G3xiUEQ+3eqM0jFeWHsCs109DpYbQfJorhXLBxmmxFLN0dRpa0UWEP0twaRebsTXSzVu1jKmNS86l9rkENmyUqqE6yh38AapL81KAnV2zRxllxzlFyHLJTv8d8aNDQy7bo1lngNzsmDNeC1sTWg1mS59BnY4bDyo83ilQu47G+WdUmzE/Ghgd2kvGCADOb5bf43D9Oo6JVDfHJtUfL+4nvcKLrfzfVIXDMdJDIp5yj2CblnyQ3R7Eme6SYRKAEF+JCpLccQ3/T7tkGsMnRDoY5u6VH6kjvQvbBesWRjjH9jcGT2XMvGWLMxRjbGZOG5i1emW9zpGFpGx5D+xijJGB5au1/1+25eTkz5BZoxTmOZ1FM1L9T6JfkG9HMm8SdFyCigLHNiyQ2Y5vNke3oXn2R0q5TobGSmVVp3YuG9K2j6nXqDLPFYkJXeqSpjiStmnXsSL5uEBRmzIiss2+wKRhIwPJMh5EJnWHgpgXUZFQGzB+cGy7oSqQ0MmrWzTDqNaWM5sahpoGzRB42pVoiJwPqIxT2dVqKc6btuGYKVsUBMQ9VEP2DzI9ZEA+XTvAK2Vx771mTXuUvnycptkm+JGfVCKLLPjGaBMyPz8Kl1QkYBs7M5IjWL0RuJOftkp9YkYkO2Jju1FE52ajLGHY/Kfk+ZNq9hcYi/DlLQoDeKWfzL/Ygln2fkGkZnxFNN5/Cd5mBr7To9fpdXmf9WAaFmaieebG14vXAjDNTRvcCEzuJuImMAyvqz2UfmdEYGsdlHkz1JZHFmxPgNpJZMYzh/Y8CNMKDcJRtjRr85kjFEMG7S36cxZDrGymC6p/w3hkPG8JOSjcFwg1QoBlnHQdkSrw9KWNzUVrwSG3gxsN+lgpnkQguMl4rIaE0ClsgM2v8Pe++1pDrSNYg+zXc7IZBwlynvkcOIOxAghBBOCEk8/eRKw67e/c+cOTEzJ+JEFB3Ru0ol0i1vci2Cz7cLm8P5znHDczgYd5hX+sy8Sg6JlgAxUljHOYX1TXcA1t/9PWwWRVFu4NFBX8+NYis8R594obnnBqI9zCe6w7YcSsliye9yS+fIUUY82hH3aCsZ9/td4axyi3nUyNmwOxE9Nq0xXnEf2J57x820ID7jd3iyXpLL1rDna3iHsAZ0OKaFJcyWxb5iedMOiwgSpNewoaDxBG6Z+z9d8KB5UHWQ/r5jmUShAx40rAMz+ISQs8y+jQkORpNT7sROuRPbgQl0CNjRN7mv8KxZoRLKS4oFoCrSGB9yqBNbCNkA84LPUcIcCznkc2y+c4AT1+BzyN85bAiQhPKGz9EzzQir8RGZg3lUQ9PhXuAY5ij5HDaq+X0lH/ahca8MKhPm39qQOZQzpWjIOWFz6DCHi7kEn2PL51jBHIJS8zle3zlgHzKEyck+vnPoZA404fvovvtYkDlKDhHT4HNEMMcUMR+Xzvch033YTItDKtNNsbaIDQp0wdYYe0+N9UkckAvQR6W8aOEypF5wGbISFJ3HWAYsNov0BcDt6x35ZrnpaACgrFjMFvkL7jXRwL884f42lZ0PQjts5szOt7NYjWdrGx9Bzj3fQcY9Xg3ElEuFBU089PWenSHSATnKzI/t8Tw4bxpGyGoRz/tGNXOyyIdYRnaLmR+bf8KjJhMtD5UMIe4JOnTMdznvbDgslZ07QhnPK8rIWLLEx5pxSp5pIYzlMs6KDjwHbl5siJ+9ZeeAtcwT/wEYlCzTc0PdHDgPZOmATW1Gb3cZ+livXody63VavMSKiTZysRIYP/C+mjPCsPbC3tOO1xPYo8EnFXf7yfMzuV/lyreKJU0KsgSoO0n4wQqSJois6DTG51oNsuLllO/I5Bhjam2o7PBL7KRjnv+qZJAJAg4A2rFaZTElT7VhDi6nSASBurlIfDcOTrM3tszE0Y2Mn6IR957bwGvv3OtsM8sBa6QL8J4rPBLBNVX4AcurBi24Z1vhUQBs0mhQm5Q9N77PyfsnDukFk625gh95wFfp1BL3xZoGZtnKjj/Xma6Gf8BY7eYW9157C77HFUTdDyB4SBxGjphOhiKIYWdpytc0+vpVSTRhaDMP+ZDrXgZKsNjCoofPwf3A+BU8h85llf/jnLBcT7n8tFSfe8h90Dkd6+s09TgM1RZgyKIPYMQxuTNHZ5h7kfOcX+nCHMpEY09li/swD9yHKWkrJZ5jPGFjuX/GKvLyjOmGjeVIBR/LwXCVNCabkJpF7GZuq2ElLlO+8DjI7P4NUt4W1r12HEfmqOEcQbQWuQatO5nfuuFUbMprmN/jVKz2Gs9eP5F46ZTj/PTCc5GtLI48Z8jxxNRYZ1MPcMdoVcZdkMU5gkwkoKvViL135+u6ggTM6pDFalvn63/Gc2jOiq/X4dEl+6z4E0b/x0UqjbOOtCBf5Q6LhWOVrmA3yfEzy6r0LT/1O482GzJWaAtQ/fh7DFBKCqHSpJgzTUdmUWWZxHjVP895/gG2WFBslinzrANO0RVjuVuKmKuwCILA087BM9bYJX9+4ZGFFYTNC6licZrc+WaUYy3HSgJyoXKq9OBr8ElvRyjoyuQOFn9hHE+/0QfjwqMPK/t2KlNGJchN2TFigxRTg8/jZAZ6MvsXvQrH2hga//6QY3CqNVgxYJosNKIRvmNWhRJ5TCdAIZPXyPA8LPPu/OzB/4V4d1L4xcLr4hT0tZygu6BhbZQvBUk5i24jXcqLeaTx8Zo/ozWWdS5DTkEuONDoj89CsUYWpyC5RSy3Cvkd0rxyrzBCkeQVp7JWwVS3LjUKTySLnK3kah1GpqUzVQaJPJKN5XAsY+7MKSjAZ8DWeIBkA5nlgsHNHaYFozlGLFvfcf2hRcafNWpmufxGuTGx8R+xHG6SkMffspDXIcA/RspI5+dvc8r2IFSvf8+/+nNeFT57j+tRrRZzCm+1hVkO+AZV5ttFECXKw3m0YHNj3ZLPjX+M7BHXB7HIYDDAxnIk/3lu/XkeRep4wSxDlHNJIqOycNsnn/rEY7CK7GlB+eB7WP3ZAyaXYlow+sVSgnFNzLWw1r/uGWAtpHMun2qG0ltTHq/PU74m/ZyOSqPka/J51oSF/B1Wm5WCDpUqPsNJNe3ntnxnEUgw0PncaiysInSjJGLIZ44HQz2fQhib6jAyGweu/IYO41lo9tXR5EnZHr5SR5K/82LNxtNLpjlgHPpwLtoah72p/RkLbjivWJ4OXPcZTPU/63WLlNNH4xdqLuQcrq3PUcFUBjtxAlWgZnXX3V/dtYd77PSPIrb3KHWo2WRemb26P58h49Q09BlkSB970cu27+8O1C91oXXS6SW843vHMb0CJr8DCJAEw3NHb8EdjbqjO0SHf7ahltHglFU/nyCH5w2zD7Ys/nSTJnNaf2fEr77YSD/Wn/sl9AOBkX90W+aWA/8oIfOefT/17R8vIPLCz6x7L/8r617ecOxgnzzmnJN9NpwK+AtK+1dXbnT5xwtI6f8+r8vpT3dl+PTyX7cBdC4p+BCy+6ezOBmj5BTEXsDq+z+PvFRWP1/AGOb8wuTPun5h8uPzC5Mfn1+Y/FzXL0x+fH5h8uPzC5Of6/qFyY/PL0x+fH5h8nNdvzD58fmFyY/PL0x+rusXJj8+vzD58fmFyc91/cLkx+d/ByaSPRtOWDwv9KEqFZSHEKy9RWvbIL0u/nHULrr8NdaOVmz+rq+1F3/tmVV7/q5XWf4Fm985fuf4neN3jt85fuf4neN3jt85/n84h7w/eM/F/NZDBJmOFi+W88gZKall/UdU/yPK/xkO/zMU3ofn69DBk+FwQB/dt8/D9cUeDeGZqP1HVKrOONyqw+vZ43fYN6b/TSLhcPhaz74x+W/sQVvsXyf6UGKPTociP73++Wxb09/z7+jQBpPOCXd/O+VwufAlkJ+HQrFnk4lsF9tLc6CP6IP61V/Yg/x5a+7/w81mt+v1kL22O/668F8cwv/0DAb8K+wAREn41/4HU+HfB8Cf/W8dgPRfHMD4gmeQ98UbBr8U+ZX8YfxobvA8w/uCEs/8d/xTDv/OoxS/P9++TuXhcCdvkHF2T/4GfwLlaWHwfz79fzuftcd/KF54u+NtdccPrrv6Tv4ufB/R/+MnKMsOdY1/CJ637n/wlf/F1f6FHhg7rvsDHOcA/7k9Fa9DfN9m8Nf2uYXRT6/qwv7Md/ekUPxfpCHxv8af/0/wY/T/TCD1aXuHH4tqm+N/ZdhOkW0v7nZ3uAS3ungVN9j07vZ63Sr8wgX+IG+zMieHp9wutycZSjySz48xEDuw142As75jWsO/HosOjlwmUyL+VOBP8M/77Wv7HxHRX4f6/Zr/Z6gUS4ylreAYpHse8uPFSYP+LEoJHPQuK7SH2tkUV6REqrKWrdUa/6hIZwT5cK0u3ZMZXKjOkQZ565E0bD7bMBSwca9qngcHOhICs7zsFPH53qk/+aqmRmX7k/MqN+9fPoXNP30K3r98Cpt/+RT6f/oUNv/yKfR/+xSiv3wK/d8+hegvP0//t59H/pef5/xPP4/8Lz/P+V9+Hu2ffh71X34e459+HvVffh7jX34e759+HuNffh7vX36e6J9+nl+Y/Pn8wuTHg1+Y/HjwC5OfT35h8uPBL0x+PPiFyc8nvzD58eAXJj8e/MLk55NfmPx48AuTHw9+YfLzyS9Mfjz4hcmPB78w+fnkFyY/HvzC5MeDX5j8fPILkx8PfmHy48EvTH4++YXJjwf/V2Gi5rV4FGNjdFi2jnEeZ5X+OSDtLhTeKoNr3s9WfYSP0UNeWevaFJ6GctM3Z0fbo6dhWvrl5JR+Xu8sfY/kaRJ+4u3YgLK7x89HvAbldBnn1UVCoZT6q2fW0q1b3z6CfD1/58PK1t+5v/e/c2p96+9c1fvfebkv6698V/3+V27v368o4d/5wfHfebdO+Bc+q/Hfubvb8K/zd+O/8n/l7d+5zn+/Uv6dm6ud/sJ5pfw7vxef6j/pxij/yhH+++BltCr/yjP+hc0vbP5PwGYOhbvO7CXD88eNON1q2aw5bibQMDUOaHdbsYXqEf7u6Y93Q9pWuHWh/8c0MM/RwGMTjHuxGR8jL/nA+/tgAwW9n2/hqPXSSXLTGz1KRfpiwOe6head1zzRoDnpfGrDVzQotq/09uFbn2Ot8doccmm1h3BPelyR4/7wk8m1vW9q6gFKoZJ3NVZfF38m5+UHmRXpAAWfA68wiVSpry15pA//zFX+qQPS5a7VtKQfGtQPWXPQhvP6H9/xeP0bM6xXoTy69Lx+Cq+lpwRl/mPdzp8aJmOt6FBgNawkIdrzjgD4R0ceIbFYWbRuijLnNY1urjpHdUH7KOKPgNGbLW0xMEt02GQ5w36HV1QLFU3pEe1NiIGAJ+GlccI8V3uXn41yYrWPNBk6DcvQQjte9w5U5I3DUQfd0+CvSOY7iOd5GLqSdHSlrtzWFp0B5byCMka9IrdJfwkKGE5nUCu1Pm1ILU9EypHyXQgJUkaO9v1+zivByHmZV6RuJTkRg0P/Brtz0u/uQn43wyhImzZ+ur4W8tN1tVKpSE1VWCxKWUEiOUehay1yBnX3T6WgDz7S1ebGT3b1rWCEkdJy66JgazQ4/zIw9Oui4ftL0YZXf0pzpMQrjlk/KgJBd70+9LSS/toifoZWhzGw3HCIewqre4egGK1q6+F3LIWPtcav9pga6FhyK/Ox7E5GXqXxsbIv9kD1I9U+IzaWiQw+VkDGsr5jIV5PTG5PuROecl6Z6VtJSG60hRp7f4pEnTgMNU2J+9tR0rXgYCBacetPH5AWlXVyiLKE8JtNROef8do4mrKWbbSwGNxuxouf6di6KeV3Pl1gPFQN5VArzUineOujJd/THSPweJPnrGJYw9cAN4GUzZThErolrJ8I1ISM3YJxESxN+F0dG1OQFScczg6UZSRrlR+oLKRKYdXQZH7HCMseTH5CmFOU0U4+r3rm5LZV0Zq7gN+8AB2WEQo6+SWjLdmK+Jp8jCsuSjl+f+u5GVCsztlaKT+nb+WyLT4nbZMzTmQUfyqr5Xgb4YBXLfJ5dclayWUby22G37nCaUtOT/gLPccjV7uxenVQflSGL9B9a7xOoZKeQhQ3tPsZ0MeK8xD7eTxMLjOtw+zKcrQrx6cNr31ooJ5zo8O6dSx3OlChAQdbe/ynXwxWcy0pPNH6kK3yZUJelCP7qqGQrfHGz3YN5cKWHdJYTxzeIw3pHRZtlRfKbCyd11tSsiREdvIdS9b/1N1r8V//jLX61jwk1V3r71ga7yygZnGLbP/PulQ+1hFKiSWRxcfidSOx0IIKq6RuJfxKir5xbUe2bSXip1Jw+WOS+rULzprHvAKxA9W6i5yXcgudA1ej9jC5CXVb6fdZr0g1hBqngqJTLNHQla/2BKDkXMhfcM2JENNRoz1Lka4seF8lDWll63BOp+qc+pQT/ufe+Rrrp8Q7tVsqZlieuuTY1n8hDuByvIRBvOSyXUERihM04tzM+1YEjaFK90eT6JpshVdmzFPo3AGlr+kxbXnFzy0eWXug0aR3oXX5avw5VB5lXIYQMV1NwNPYgtuL08NFGcnNmf69UPnoCEY3Q7ZjxeedBV3oqLpRDharp8krqOUIOlmYDulwSt5jsktFhEUfUvaezfVUuYIybGOLVpjHNFPxOqAK/LCj9aaBDiveCQ1BRUC1Id0eEVSk+85RQNU4UvGW8KLOY/ozLWZrZHFEext5vH5mhqCSnbyGpn+Urni3StL4Dg/BdAe9s3j9VxnGgmp3dKyM927KsDZgg8gV6FhXrhWx3nkKH0vteM3stjMfE2jsZicoqPbbEOMqw+tWHXLKJX0rtzbjsHM0573d4HkM3bHoirnGMYYejRZ0niF4jWash10IzQ2Rx0jKkhuqiSjoCX0u49Oc4a/Ae5XtAekW8pXir4x8XvnaKIEzj2mPaozXIAwI0AANTFJPEp7f+E1aXSE9X82CiRvvxjGQ9NGRlJ5V7Ecer/1cQT0/R2IaCra4mE7oAFas5AXjtl8rqgyEYnmHvsKQ9x/JS4v0n624bi1wHZFUx9Xz5Y2u0ULM+vGxSornxhKBnqcasX5nCItlTKeaEFGIqz63/pIISi2qCT+fjJ+P7AELCQsmTba8a6xekHqHixPDBKvmtZ9Jr59Qqeg5GKhjfTTyJdTFVE6Mt6jfWt4f0qNQ3nNKq3n9T9sjyx0wTa2dxz/qhYN0GfI5+u8cUC9zThRV8vue2S6pDB0n1Vy12O+8t8YckTqMEnqw3+UH61BAK/0aspaz31Nmm0hKCyU285Cdu2R0fCxSCTfVtnysko9FuiwYsszH4tJEAumO8ZkXxM2UKYVhgEhNTkFljT2Dr+YUajCWoixfndmlzz0YequU8MOc8ZU57aABXZU+C/y/dXqe3gcUsm8GWUGBQ5TDQuHfoVWrc0/CWGWglunggsL0UzNSoFbiHqpdh5S4CMQ1mbTivRlz3qcBMZ5LpA8y26qgfQMUVoc+PEPVxrl8YJh/ixkPi0jPB3lO7QIFhUzHK7FequUYAuzEHd4/U6te0Ce6uWpH7UEOymecEdtPDCesERZiYplo53EqY876CPmgrAuwTQp0YpFE8E9GFoPzlswq5x7j9DGf1RJIL0Rf8+hJYIOJnQTthegJbA1cm1ChEXeJ5TXr3iHfCgYpnfQhwUK0o/JTZfXfW41Wbt4wLTXVdN45eQN0d1M6ihdKx3ofYjKC2qdmpGr895J2AOnI/tQ9lQwqNsh4dwyNjCEzHlfZzHrQUlKvV4J2i3ROxLtgkG4Fc04TKC1ZHdsnqc6sfmnCsFgdWUUiLTZSJWa9XHgdW3VOChsbUCmavsixNyFjyaR5KkEoy/t2JqFjGXQstZDZWA3p8OGgiJ3vd6wJ6DO5GnJr1lSoRpsj2nM5ZucryawDry+Q+vlYj6EyilWBDr2MDKDTnsZ/LB/3doY13RRWYJxbPj7+D47ZFkKCC1/LR72Q+rzzglrkfyyfG2iiQATUalDyOfMRjAhVqDnfg810L0fzSD1fu6PW1QJakuTQ/Y+Wqs5kpicqzALQTi6pdup4dE22smV9mmgzARUPTVGDebxkKyRrwrRBNBQsB06sZw/pnCLTziX4szhRvBVYL02f9jXAFtKcLlqNTCwiw/6B2RF0udXvc3l87q7xmJ6nyiwkm/Quuo3SV9pds2noi+8DIxTWj0cvVh6Kr2V7fht7daYG+48EE3jUKlNRCh3wnFWmZ0GKz202obp5WnV07cP0csQK5ci5SqOJFa21o7FfhE45m75O9qQVFaKbsPq3BhpCd8dFuYW6rs/1YXI7qucT1suaZGjF9iGqI5eemvxhNls7J62XqyS4Th/QNTYk1K2iHaO8REuxzaM6d2SWZST0J8yDb8hmXSNYf0tVGc/WOzyprdgHU50cK3RC26dnlNp+iL+CDV5sQVB5ja0ruvctIJ2zqI0SHYaJrpL+tyeqRXoqM7p0MKCchXAJ0WGVXFXAaedErSZPZi/JWYLXstCeCTrEkZnE0NMhR6zR57KjelKmprCE+yN0LevzumNtHivYK9bN5dtji3TIXFwsE9Y+kH+uHaw5uvYb1Jp2VgulUIKJ057g7zqjz7ldUtw2a4xwuuNEA2S+LqcQOrLEN4rPAboxfZIYbxffXiLzUt5b4URekikU3swqPCqY9yrFZdyqfW9JO7BaFxbpaI1yh/U4gx4Nr7DIzU4a/TXORqD94s5ACwusB9pjEUkZXovNFFPWl9anFoODz2gs7grokoPlJbVEZCY7zEgD+ihyuRdlYfQTI0KL2YX2IAV8yETgdtAIYn4YAjlBtfmlKp+mc1JnuKS94hiXKM61Zm6nhXR5J+DrQOqV9oZ4tjYYjPvnzOys937zXhsOYbc3ysHkaHBAu4lNuq/6+RBBDe0e/zxJLPiivJ7Wr6Zen2iPxWRBTyN/7WCRu7l1kIcN1t9K+ybX20k2B77WzydiEZwPO6j17pxMcj666h2eIMfNE+lfHVbohefawdWxDZnLvswK63xtpuLjtSRdQha8D+HGFffvtgvS0C/tVPVe67kJzGcT7BLiwwrJpa8B65/jrAxosPrEtmQ0wnxEy1C1fuNHKvhFvPPDnbz98WNBajWPlAPrWohlObgPXXflnkaKhk07AbSryefw0D2A4zykuK6d5jL+6zlX+tlFOgBOldbgWgM62BaFtRIPxWZ0jOoksHMDc3elf3bCDGCOl9ARmBs27fLoJCu4Lyh+iqB4gkKYtOpmNmEje9DnY5Jsn6LUErFm9rTLZ9i84UAvFw0OFDpr3qp8ngwnZPXXsLlm0XTVi6udQPhdrjMnulfV8MUaa1T6LjK7p5HL2EpQe0GWjjCn4DpszokkkDl1NqdH56yWMKfRxlgPikLTnQenGZnz8YSB/Uez7oh2r9AO04oShZ+kO/YpFkOhbPvHZnavMRU5wkjztSBYan4Olgg0SKC90ZX7W7WU3NhNDYIxojFYfYRVuDYeeEfiZrfcYJunHVmv8RZPnbxvpaWzXq9ZRLutFsfl8Nh50Hd4KMTIen3QxroDkxdCF3Pv19wNAN/OCeFSAu3Yo4QttnOPnQXfk7rggo925e12hanOyt0FQNUBd1P1PaWy07aATpGKP/2EQzgltZgpGEPai3vsxrGLOYVduveJJ1dnZS9lrRmCDr4dMc4hDXok629vGJyhjLiepbftXBUxtBvoxDK9vtESLlFa6pB547ev2aRZCwlAYXTTWqPVSjeZNkOESSlBYjC5ORB20tRdYAk+aKfv097A8sYdCrP8/dHmqyaxd5asi3IOWgAaDujhbf0TRKlW2noqKpjxNxhco9fUCSd5qCTKQAXaudHety8WexRcdbB/Tx/WVRLgO4qi3Gf26wSSorWh1eAQQ+9JcEnJk83EgKa/W/+Fp1bO0zYcjtZ5bwwxX+xqspqGr8aOrnWoG6PWKoPjcG54jo7lSneIpB3xJ8UjIqNe3lvXUrlrkGzvh6N9EbQnjBkbxVOX7/N1+qFRNYFaIWr4rLFBiu313IkmgAEz8DidblEgLXKtVo8CsQEYl3QT9xVhA7TNcymdYC66xMzimk+GRA8F8jyY5w6alJIK74J9htMQrPfohG2LK6gPPnhNzPm8Erv33GqsyoJ+oZIRA5190qdHe9GqayqQbsLAO2M02igjubVPGRZJk96mnanh7GNyjqkXUa/nhXiErvN9+46mgCcG9/X1oIXE4P1xsolTRhRqGYWas8MKnQLOzZuiFSHzV3sn7jtCYyytPdWXbU+ZyQWd/dsXW8dqoaF/4xIe0w4xPoOCIkTMWxh6DfcMCWGO7HPk8zhBwd3Aceej8Ozwzhlb2ncPOhdAmbGvpwcrG7zTAP4CuurcP7uymXfWUkYY9/AX2JRaHfI+4j7W1/Zayn3A2Z8+4liN2AjMP4ZamUcaDQ9c0Efut/O+oQL5DF9RoYkUMfSRzvcxAL5eZREfy+YxHDsDL8eaez88xCwRLKjBfr7xsUwSmSZ/ucNYd4/5AOU/bnZrGmEV6sN9gBmS+FgirKv4M9bXzb5vyzzfpjy2IXHLNA+m1xzFWx6jk+ScHwu25dA3pvfi3T8C6NUXxdxnqDFLC1s6WFewbqQtG1CAyo5Xg4YOsi2T3jFkxj+d6h8QFFjzqFEQ8b404aVF8dhmUUAHBdzDeoDeIm7Gd/HgPraBYiDZLBDzyef81ENTAh30w7Fky31q8xDvIpZy5u+6cWjAaqGfB9I4LZhMmiO1BH2sIn0ryWKu3KuJvwI6rcohhd+8cZ92hr/C/UHYbOfgmA9GegesG7yJw4Itzyh23I9tHrAu9zgCpxFeg2rm52P0ylksy799G0yJjYCm92WHOdRreVJHQWd27YJOcilWvKfVHMMmr/eDCjo2JwLzaLhck1YBSGBTaGssud8iX++G+//nKdi/t5hRdC23vCMYsSmvPIbhddwH2RInvJPx2AO3mtBZm4KPVuMwGfGeJXIRINkB+4DFNzzmc8U2CrZPsXzjY5053NeahC0LMIBpvEL6+hS7AK8KGkiysTLut8wSzIFa5RshWPOxAjIW3hoba8RsH+TTsSwerXb4WJDphW1pmcdXlICPBeZcjbQbqni8csK6ABNL4w6d8wimfDsR3YwMHAYxp2kyAuVohLIcpPH5Ne6rnsI3HP6NIGLrVQWsPbmhvKA0ICge92lvYByXRSVzW6K2j6U4AEON+UxvaMZ92zGghMl8IZhHsX2jJfGBo5i/V3x94CH+jiIxNEUPDnOD+MAR8ydhTZTRZ2mRXlIVj5l53C8vU5+3zH3efA8+6qnPm0XyjJD1zpG3NmmSyjichTzeDTMG2vRRHrKYgsfmFmj0g4d70Yr317LTElz5J4Y7odZwf3QMX1hYjAMY3C+lpGULfOTGObXL/e1ZJ4MrxqQ4pWKMYA0vSWBDDS0Go68wUqk/mK8JIrPAPveT2chOulElZceTPzCxGeRdytUpuOnHs6CUnesnhxmWFcuT0+d9+FBG/UJ/9oqpJ5G3zfuHmiHILgFeBb4ZzcvEj3pd3I9XrBFIx51/XBVjMCwDfUZWCK2kkIHezOuE5jXnOSvSF9DslyyWlnJ5OSW949R8yCKUZAyCYeqbeO70kUfluFLnLE/qgCWOjZQ24152ZcWPRCR9fo23uIjUkVgQekGvjnfnnJJuXv5DCqwbpM/AOUVfLzr3yC8A+p668pfLzmWRlXeZsvhbS/rOyiWPSPU284Ba5bMm/vGc6yr2lGKaJY8a4d1vO4I9rfFdd8k44icRWrW/O5WzwpYn1iHOjBpixmu9M1CJnWcMI72K6VMj0rlL9q4F/53F9nyPeGo3fJ2Bx9dyJ75316MxR7w23g/6RbtyEs2Oetm3jD+0GO3AJfsurZCNXrLROtLTS+0TJkcrngEhkZ5UssTxNeG+Wm9DenfVBtPc5Jx1lVe8HGjCANWRHHqbMH+7Ab4kSMdhfCjlNqkqexBhyBWmiXn4UIlokPMVa1FpSXQv6XcviOxFLlmc2eN7UV/kZKzeY3sx+F6GPem7ns7ZXlS+F3VD/KaeyvzOft4wCV+TXnuGWjBfVmuyuM5KmZK9fBDz37vMD+pixIa92CXbi1zxvbwI2NXpKaZQHpQse9Opid9bsmoqT/AYLLYhGcTVqwYcloOIZne1QRaBH3uqsPPXc649Ts9w/pay5PEAbLuQo5H8mpzZSnlRajHbI9vnlPBvS/H5d6bf73hkXzt7QXmfO/aZTx5ID0tUTK85lehq3vPYk6yDC77dMsmCX1VDFmXyrsTHrzM5HMhrRrEVG+1GJaeSW6x3/Y70lZM9GqNUsGFMccCTM7J3uxQIDrR6S7mOjVUm+Irr0ZisIbOOkrJ3IfuUN0yLbXUmhe+sF2LSUnhWLtRmIyadpdiN9uqrJlqAroQnNvG/66W3HJSPeb8rSSLmaHBctQuQP3eWedDuSZ9AWTo90od79+r+ed/bamXTuIASMZ2hXbL3sJb+uJwo1/UQiwd5G4P49JWaEo2h1nQvWDDCXpC6ipm/csWqQxvqjcB3xzTW3NWoPqXKtKuqnxfMo32mXjY531I6ay3LtcBbhrYyYDVPY/R84Xq8zLIB6zDYb63EtiGL9Tql+MJC+qfmMo6uM0q21gf/51vPARnDGFvHCSQZ1U9CfRqkyasZHCpJTDV14PqeLKyf+K1sE9zlkT5I9F2Nf5XAyecFZ3I1wJJP1fs+2NLGa4mJahKrAK/URgibJN50NZ5PSY/nUCarC2LMwjKY5mUe4J/q/khvZ41Y3p9mn/X9I73r5XV6aTbnHABsZpuuJKMwAa362+W7Pgu+5cxmW0yOqXurSP86ovLvHif8rffk0NfkxBZ6hVX0qsHoIfcesGPwHQV1Be5WrDQjYzcbVttousS/Q4x3N74XxfyYgIZmaCy/rktXu+war6WNtJrtCSaYnyl4G/A3w20UiJcZwmfj6TRGnSPzmUiraRLYD9udXQgcAmUw+FTuWWlgDc7wXlTz4xzmmX/nyVa7gxkfpRSPbcwSMpP6EV+ZVLTttNiNrMzXl9344xmAJzRnWEFafH7OzondtJAbMiCUqqJ4AEfhqthgC+xjslzUx01KeDHL9mu1ar13z/Ns17ak/1+ur8CddcG6jWaWx9HjMQ/mG4rN1MZttWKx3zW7jX0pj1IDuEN4o9JOXoCIxbDFz4qgH2FIJtdWoHKN0KLixfJMvVbHj9ihBelrab8I9ui7wNpjSW4LGnmPdpvHprD3GAcLsd/lAX5pQTqRyvmQNP/TXxdPM6v1yCnuxXX+hPM/W1RnT51kvGu3B3tdDU7vGeVDqqoNBHRdLaYhME19L5uX5xbOP2CUacH5783xTto0mNUvcmJtYazRk3VoPWrXKvDP44Egu30L7/drwoUNBBlThdp7XrUDJBqZz5mzd81rNZNBB5G1p6TNy3M/Va/PuSrtMAe6vtetv1+d4PsqS0dAyQDwMsRcpq+AqPKbCMP5NRSs0yfpIoB5ZV9S10kIHGK3GQ+m7nHVb0bq074f8whO6PBXhfXWOM+26B/Z9vnT+vud/K+bAGr0962FW/9XFr+2+fvGgfd3Br7x920B9e+bDVgm/67lv15LMDrR2xqY/03sRhcw5svN5l1Kjg+NOgfgrZ89zb0dvHbAjwbH43x7dIGpj2erYdyPIbyzezwLocr2ZFhn9/4Hj0StR2h63Q3PnaUPgdE7VbAf90vbvTi6UFf9eg1hjis2gYxh7OurzpfpjYizNeVZmcb98dmdRudOOoqJ0L2Oq+H5tcOWR1xMJjNgC/LIXCb6PqtoLpN64tmBrbWZWdcom0Gn4rg3talZufZ7Itg2y1evud/RRUYydM0oGp+7iVA+x+1HaWfrjLhxZ6O97d6bR6MufLrX/nszZf1Q6qGwGrem9j5PzPO7Wkv5rZ5eC2V01r2S+ln+ZLlOkTjWuoko1Ku5OpzN98N4GeRSeY2pDwb5M54TAqqIfAkO694yj4f47abOSnTWk8vztTnSPqXNo3LiCT2zllvTSPnMLqZwkNX16uzN4b2r0nsvwRYOjtXSbOLvrR83iNbD2aw0wkiIgyQbmWbZe+9q/JD8nL6S9cyXg1Xo/vncf1aB0MxzEorTPE90Enu2f/gDDD+28kXPu1oj8o1+GLSNdU7MRk/Mcuyvna1rDWc+9S6pKb+LYDiLq3QI1GHfPFvX348Sw1uj25maqPmZE9Gtl6cnSwgj5gOef+8Sbe9P/73fe+Y5TeOgVgbrOJsk0UZ3M1o/so2077vnp//SzTowj+kbSWZn7QfP8gDKHz4zUs8SpX/S9evPJng+AcmLrhs+MffL7ta0kLr7i57khvnXVRSdrwMt3rnbDaXwnbXgfuyB07yGPtzdOUlDA4RbfvpkdtDcvfPnXjxWI3oHIL/y/Geny46QBzbAAkb/SF1htVtxPzorbvChHlf0yJkZrxc2aEDmebHsRur6czh9eqPcHWmH2rly+WZIuwcs//Cr8Qm0AjNejjaymVcLThD9lnmA5U0HmgWqbswBFOQN5yjv5mm/9kdTS6bKuD849BvKjsMTaD68Tg9+VF6HLtLfvbUUwsMqJcFqpI50fmLAdt7P/fkQ4LefRYvU7H40F/2fPX47ZStXrJXIexRcV0aNLNGJXQsUMcyO8qFMdElwotEVbB6Peij5gv2ZHux91SmqHC+F67y8t4PlWn+qlGeMuE/r4xXJA9LlAxiy28l2kc5EfeBTTK15/GXD6L7sZvtjKHrtQjgkUjX18wxRS523YN4+QMsQO8cX7+fxtEb1ZxHtbvm0bmkeNLfUMVUaTgZHff3E6909Ibvaa5QKW/eb23yBS2JFgOpBXyr4hBIqR9ojo4IcKwZjiLfm6+PnNtMOaDR521JfRa8vXhvcN+Uh67JeTi5zSev94wowCKRBpfTaKyGvWr3L/Ce5jQngCbg7eXUYns5HX/7cydxjub/YDvf3R2yU69uJR84rKi3mnZHDzV443PM6X6+nfq2L73LUChgWL4XzbwYLVes2L2FvJ3dnWuc7dNNhv876znKCVEaaLVKI9jh5J6bUhxM919KX8xKWw+2xCunKwczgdxNei0EzODQv76j2cyTLxa4Sw2tkjzeEHxnyhFMJ6K3YqjK7p5qqUSOApThe9PrrQw+xX627A8QJWzAOZTvS9AU/q3z6GK+3MYjS98P3Q1RFLE+wRktq/1nIH6PAKvv4/Qy19rDe25+Xv6/EwYvh5YDrBE9bQ1qlDkXuLXL5DRAF+RE+4jeKmOcJVTkDgw43uq9oyexe/DvLRFdaBHapPbKZ5661vriTgZ+5DZ/Tar4aPqRXyrB0ZjJabc3sMRYXr16HDHRlJl5AuRR4dGHSO5REzMIIbmgaH92VkSs1v8sjqTW/0wLfW2JLCiRUoUOW5fw1BKSPS2zBfRaEY8vTK8tTlNAbH5cjHWdNhw8Fq7tB2TiVk7F7lEe4dEBz0z/98zaLr5kDPcZzYziOzb0y8i26F3N6ZTfEpRhbcpilyxixdIz9hoyCRzixju5Wp8wm4Ljw3tmNOrP0EjyuL5SRnuabXXPyTju6UsTvwcWAX+XdK3djQW41LCfS+pQIJOMCfWyBrtIuXnU1EsvuI+4+9QqtFG13x5bYEuQoWWi7ZB6M1N45zXng7Vtx6AxrTLTaepy61pbtPOvnTIBuL0+/UffILLfwngYJM/ddDak/MwrJVm9ZhEDejCEdBLO6a/853VDid/uymVdOPfJoxLj/+sLfrt2csfWBnpezGsrVSLH3mnm5E5o1VIc7ujduaRitEjcKxPERqtSc/2n0rDUjd2fdOiE8qOVdoFLdJbvLTrt1igJ0n+SNASdq5xmT+K2yWu8nj4WLNUkVz3+cDZt4MdtfwD5DOuc7svEcf3aYniCtGpPXtVlU/VJP9AXjZYZX8Tcf+M1o4yZ28F6BL3tW6HttfhFoVjoPCqtobEeDZvQuba+cbFts/77ay3yF1SPG9Vo/5+cZOYf30POFO8GRGk/03I/MZu+dngyXmU8G88MHCQEcrbqPXk/kyVsxXoy222cxkCkVG8zrkntg9WvTeGLjk1exjNirhE85X25r9H0V4q9Y789mma0sg5yqU/AIRz4PF3hbu4vzEu+lfaDagLVhN5OQV1y8HK3U8Ahq3yskSNXaDJ8tVc7QrUTWR4l2z8TaYr2NTMuxQ03nD2wE1OpNVBajLLmxaVmWvqE8wTt2tzDHH5Qq0nZXq76cFt1KpJxcGXIv7k4DZqo0kV1eMTXiE8U8V1wetyOH5G3IL67bG4PjarIwMDNWRcw7ETjqNPDG7MyEQcPncTtjsF5N1iQlY3BTkLK726/Bcrhh56BueIS6jszpcnat4sEsxed+nby2laN9KI1VXAv2tw3WMdW9vj9NBjkG1O6M9xMtujWTTOpqvMgovs2JrLmX52mR5zFq1tV+5Lp4IXlIueVrrdGuFC5aEbmo1nr2mm6xnJy8e4YRJ4oRXMvL7QWV79hArDKE9YPAvTv7wXp4jwk3R0vmSUbKK8HzN02u9s9kgXKNyEHRzZbeasPOdMnlLSCt9tghNTf2Y2wy2PtLKMAg06E1pWeljx/v9j4xsK2KAvklFpvN7DyzqF6IioRFyN/35lljDNZFe0k0p4Bn9Kt5MwJ6OpaBNpZlJB8mz0Pl3CbUsjLbgMVT2p2x3ptFOqJ4L+fRFjNxFfBbINqjpILc8UnTNiyT8NvXIhlZPQS4lMHs7meX6wrbRgv6d21MoeeO7NXuMBlvJ8qxEnyIZfwD692MZZbIx+V+9wp3N2t6XwxAVoMvTy0fEANifBdu8tsPwujmiktkztPxFEwVQ6cV481upg6p0nHkVRWCosZcH7RLvT9svGyLCWU1cQ6uFTK6Mvv5Y0JvRL37K9aJj/vlvgANAe7dBMlylVUk5KVK/C4aeCDf1GNaXz8Nuu2QXBxmQjUndlxIzyBkNzXw1C61H4d1xd9uHn3oeuzsJY3hr3YBWdZR/A3RqRJg3Wug0uK8ILjGY2tk9+DvPcagDU2MZKNjAGy0O8Wc/Rl4dWv1S+rpuzYbBWOkao7/UJmKjkxvTp3deNwnQTnpezlHbdYQb7C6x/CvCfwrmbm3vC7EEHX3slme8Zpyq5LipxWG4NPy1Cl4jnOC35WNbZ5vVLGMINLnHl+2+34lz5bElzeUFlR0xlz/MHqWe6HGmwsGTl31oMcLWzIS4nHBTlvt9td4P8K6o5I2tlCCab69pohgyGfqBeBW3AlLwpM9LOleA78uO3GNz7rFZsnwkuhFRPhPPmQeNs/QL+JBuszTqaXhcSH5cu8vo35DayB8/UAJxdFHYpkHLG9UT7SyMhIGS9G+kT3fS5oThmQfcyKhEBdYI4YYljdalrdFSnNxdteoo+f/IvtOyo8EkUblQNSxfkIyUWXCUXZasSEQba21APxkV18oCo77yb7fKJQLWvI9U2i3nESLNq9xFoVYFz96g/kVZAv+lExTzFF5I/ys7pOXCxpfKWUpVvmJfPXMcdPlKYmqjPvAeo1SYoNC7mcFUpgctKePWKyyuBOtatReDytM3xZaawuPwLRwyy3RK7Tt7dwr+LmmYBtGmQwx77jnhD10dWXTACLqk961jisMf4dcxBqnj3NNFT1ICbQ1FhuNINLQ6o+Zi6X/hygkGK94XNSqsJ1NqWelZiQ/GOKXlMLlXLZHWqliDQRLUcXfl4RGtFxi1ZqMJA9tLBuWyS0lMcJyjVgXoz4FPpPOm6Ys+i22fanigJet9XTsGwmBKGptz8oVxpNnQbGs4neINFTEcFMr0olGgQmG+iQNvCmYxMKWaQ6xHkXm95Fyi0SgjSRs2TKooZ1bWgjwPVsWxSn01S5Uy7I0uN7nsWl5ltqrbDWMm21csbNCTFc0+ojszU+G5ajfEpx3Oc7LO+uGx7ML7UXgVhK4vZqDSZ3+r4lDfQDGBYt9Ypv0BOOdiyQOEr1ktDYdsboI8msFMrJ8z0kE9V4nNov2XpjMSm/qWFkru82rTeEmI9WLDOSyaF1rxXgHEHnUKKadmKROkU90sJqEZp5+ffbI2v7Ejb2pRHCjsoNjt6GjGTwO7YLjcluH4iUHfidQ7DE+EJ/Vj+JsQOIuyD+U5949rEBPf62ng+VOEwgaOr3JcSHBNluQlsBN0Mjt62HrERu2NW8M19TVzZ0q0/XehXyaRmj6rfMsBKJ7KC2v1rNSNYIaV9FIRo5yLy5Us27nN3YqCgiWog4uwmX4xOfZKvvsntSbRU4xBkwO50zzfCDkjUDikQ2NnhG1v3V+rqjCWn+ZYizBnGlWvoFL7DyVQmfL4+hWROLoR2k1wHxmTzSV3Gb5XJ7qXreHiXTCD+3JeY3tzWhIz+3C8spa9BaWHSTQvADbwQV+n02wtTcVCIqLwpRlbOBX7ti+Smxj+sLcFimj4XK/fuWEMwbgZAhmn6M4qAlOXh8VuAbesggSsVZsMT5u/1Cq2gfMujo8oIbPawQlGlYArQ7rOkwYo6naM/4nXx1sH7vTawwU9vTgoMszg2Pg0fOfYzUf4z3EecJpn0oDyOUpP/uLuBlT6eGO92xeCNiuq/FCvAHc9uJ9MzgMQ2I9QFSf1VwJcjAit+X02WMLFtMVhMkmq0lmEc47NZk3IygAtOf4QDi0oaqfuhkHCSLgOvolEYRHcUk9G8EYZAqY3nsPonJ1v1zF0dJbvsYUOonoUU5ndgaWsZM4k8iZ641wnScUCb4Wvzo+Eb8v0wGQX/Q+4Q43AomgFCgE7dnw0kwEyNM3LFsEjbui/kUSF6P6kYm1rlmBgQyyX0XtHY9kMvmA19w8/slpNOUN9oBtgtGAEQqbF9lLXnRbgWg+t7lPJcX8PLiDzz5bYT6ZI31yOSQZ2UagWlSmKuHd+ezEOHGJVYql3FKoDofPc7N+PnJZgus/8hhivBTq/KZkWUQYvtLlsKrt1No05eUjmoMpVfKS8x29jesTIhj+JBSntRmvt6CmOMHeRvvg+BxI07f+vAqb/P6e7JfWOmG5FuzOvvJZznavbANq4yp30pu1T4yCxEZmx9lq9HistEkLrq75tT5EauxexZGHbc2puhCV5Qaiy9sQMvXVKqGZ+uhpL7FCcu7v/QqbhG8L3NCdJO5me/+C3x7dQd16wqWMSz92jii/kNsik+MBnL6rpXvbCTNye1D52CzT4fV+JO14nEz0XDXcYrJ3zHNIxjV25nl+l1wgtMDKxUS+ayg2rEBMlousohmTI+Zta1cY4u59PsX8oPXU/egsj9fxdkw8zP1ge9Tb/UhVPAhfZVlxqOTbTxviY9P7s7lePoheQxwSmLLdXQ2awzlyUqCUQzqp72FdqDNXLzMb4/0ZdPJlot+JjFJSg/m860GwmqyMOOhbeefpy6aRuyoejd/qzr7b0lsdmCWEwl7rZa4NJ8rOzqG7F8mnoTzOU94zZTleZ9vFvZbL9TgLTBJtuJ8v3vAQdR+5F/snkq/d7dG4IcFLj2XOYclGPBultUjNYVPeO6AAr7heBWMShxBfMdy5qsUriDPjo7hhm4PcKvjwHBS/pjH3z8nFRumwnEOUorPWI0GPr8EhnrzgJEgNCuveLdQHhMeDe3FrPsQThTJ2Byw97SBieSVOOMxXIR4Td9LMXy6xotVBxsmjGxexk61RPxuA+hqhO5r+Y4x3ID2Eppa24VsKrsd7cu2EcTHfHc3da5QYztHwIkl9zYgEJKJU9qgXUkHgrN3fr+UrXqHN5GSbWIUHiSsPwLt+boZV0VuBJd20K+io13txn9M9KAtW6SJ9uXaTjNxpml9z4/PRzsM9rH+L9T7ItBB3yTVyR5qWjifRWaqedalMp9A7DevzVGtpjRhLniYcmRSzRsCZvHN6FZdmtrRKzJXX45VrMUu1jZhOYT/A8vQOdd/HLwxu/O0ROAfngwEBXaq5wTvzsA2BmUmbSCeQedvHOR8yTxtLQ4JIILX33JVWo8TEOHweHqp07gatOxAWGKXAJYPlgw6sZlZSiznhtOGVd0IbbR+RHfj2/rgebt2bAjGUz1FJD3eEaWMU1CsUVZN+Yl+Lpif5W/yaqx3EV8keGDvN/PjixDbwKcKVk+1kLr8nxe1ROSdGSRrPTR64q0liKeqoeJ5m3vEz6gR5OZsum/Fl3q97xxy1aL57jLC2n9Qg/1W0ZJ64yupJRhixH7H4qOThNZi6YpBkx+RWTpOn/dY2xbLpYxcwl8RlPLT4Yi6Guoqh/ums1hD3oyAQu+Hp00rN8Wa9BLtcEaTe+d0A84CcrfybVT1QMQ9YZZt8CfHCEnJRvUGAVvfx9UAjNvO55J/eleQfKqekYeHWZ3kGcj5bDl3VJTEUbKPdFD/Q6+OzFmBDR9exzjKkxYmRTjw2hndlCWdg+zxqECaTUEv83QS8y6/4km/h2fYmxhhZx1gV+cTJxlExvrOdyzyumz6PY+FR19IjfFSjYmKvNnaC4tAbP6sJeObvCorvzUHEHInRyo/v7oBW5tO6jbzuMowFMZTv+vyeuudAlLyyE7Chl3vVaIl5QPwhfqRW/u76Aru+ozxPN59Aeg4EWwgdq9K2a1vz6a1rhtLyYJ3Wfqko8skAhf3yDvMije9ZCV4wD934iszlvoAQHJ7DMLYf/NNhpij2Sbn0kQq0OWceYWzzwOxQpCRNJsH47pXPsSATF4RDvEbahkZVPPnWyNYuagaz8j6vnMfIx3gw2hXTorDiLisXhPpvzGJO1QQ85vb0Vl9SHSScrZar0HPqKwTvzPFirM5Sz/MUkMk74GI5kK/i5YyLLSC3q1AhWIxlyk00okmeWPe4Aj4CaUHyRRlboqsZZ89eLrqFuSFYgTyad4eFgqhX6FToVZ8AGmzvhZVa2rV6PKTiY6/v9vEZGuca+MexR0ROqyOBmrUyZIMshypy9sJlDvLCC9etCsHP4XsdX8PJuJPzzyERBsu1XRNfmMUzQ43TzDG1zWB4Vkp3EGSvjuhaOQmmj7rjPa9Ps+1UztdJtKyZ/hmyeHbqq05zwUrVSrANJ34CLgsnqJlIgrR4NVKeHVPnRAPKxYDa896FnVsbP/G5ne3JaIva3cXNCsdC+m4q+jN//3mWq+EbWXCqs4MAQ8r5D0886mJT8LNuegk9NW7acLciWWDIekzcU2GOBT3Rb4TuUWqxL+WDBNN9Fe/JjCc7a8LtzlDUddtI7qkxRgPq+NMJfL7FDb3kOo1J3hcmDlPiPjaYsNim0GxzP97I7ycm8Mz3F91GIhqoYd9IliK4r66bG9QiLPrFtIgNeQegnYBgBs1vfYjn0hsNnsJjPNutme9hTu1NlF4CafsJZlv03D3c+mIvulWxMWRr3Qe1fa5coddPiokVx8bdEgChXmc1ImSISoF+/db6Pm3FtLrVJQgJy0g9+ej2n1jeGHjlJuQmiQ9IwdUNmiHiLIfEMmhtJ4vCzBmKynrU4L2fwjw2XMwy7xtbvbvTTY7HfWGrKBoT/0Lu6C3t/qyoAsCvmTnZJVWaDOWVNzs0Zov5HGRg3GXlVUyrPjST5TqrSIaf/MpYZ+gQqOKVS3cBKbfZZtGtq/gdh8dnANrrxBf97OCnzWMSPfthaGqKtZEe9l4ZWC+0uqDDzq4b5REs5a2glyvTqOaF7l/a7WJaKtvULJ55bkSyuDD6hjh7PakpYwMYtzFsNlMtqhSSNogFyXxSWMWuInvKZeb/lvzhoBXDRDvOFqicPJzdOEPv8iYvVXsqJYl10MFMs7PAIXcNT2/q01fz9W6t7mUpeglPFK8FlqC7cxTZufWHjdA7Df4yUpPm8aGedjyPQIwzUulg32anedZffbD7TLGuBjW2qR7+ceeB+Bpv3UKbPVrviq3uUWITt5767hnd7WfeHDMD7aiHStI468LNYQXD9CIvlcnnoAKjm8ZrlJzao5kRq2hqSx7hw+YpeL4MFXzXZ3OoHtcpcMlpjEyv2+fiGRZ0eTTdcWApWIHDhLhIiRUs1u/j8S0Olh1xRUrNepteTmYn4jNYJAOzC5aVisJ2BDJgXkln6d7Z7dKZRvVRPRGaAEVXZtZdtlZf3qjTu0cbZoP1YNqEzx0+v/S909/B7fCsGzP9nD/EUz7V+erzwMffsUIv+wCrNd6XclyG1zwY9Es1PnbH2eu0qumV1ceO+jhzO9he0XZ0LqbXLseUFt/rfI4VffhOdvx4wcanZ6xcR8zZmDSDj6GZipf02mOC5NfTGTnv91s7Gg/59MlO4jadnwiaS3a8ZzVEnN2WBhTkZVtKjU1W+KwitFKvm1HwKfST3jmLbabLQTqbZT3Vhe9zWaOSaPJIEJo8/JjUqrAsbQi5sanfE9/yfjszrnIIEklo3RWJZORLm2nB2qV+1sfJEcOjTTqzc1fPGmnz2aPvzxbAdZlbcP/p0fUY0fyiI2wqqzqPFjex14FWq3LgrdZpqkW2p8hnc/DRO6DWM6zgOgnt977TouQhGDC3zef2tVEalvPxdVJX44WHtP1k96ycwm2sRg9VsWkH1mkzP0HVg7maXpnd6+zhZvexU3vfyucYSu5uF2WxarzlagN8fEGQV5sg5mfPlsnL8/Hmkn6dJ0vpnJ6vw082i5H9EeaHtLHO0oMw9uF6iI15g57R6rtOQ8CUeEhuc+0lPvzCkyytUQX/cQST8ZFrb/si0tzJicPsvXNzK98gWDQgchHvynn7/lZAb2veAdudFH137pehmA6n+2Elpz1ZqtIbBD/kjz46J5bRL/MdkF03ihPrFYVoMqw/p+ajGLJ7kDB/hZfPNc2Oyw2/M4qknohwok23G4xLpJzV0TU5uIVzGKTK7Gj05mPjEY0g/dCIrDRXMqzovTMA+PCztIw8sOvuLq+nh/ViXcniq6N3rVVrSxDA9IJrJt/Kydw7RneENvsmG7wa3YuTWpJ86v1QCwNyocXxeDx9kN2pmHwVoZDA+FzmtjibBzv/GrbosRlNJoVb3DoH08Ch231uBHZzJbzSCjHy7dW56HNrcxS52+vcnjzbpO6ciq7HZjdv0eEzD8X4sSja9/4AJcTdOsJG+KG7K+1lUNt01EilSb+T+zJ302H10vYGhrwTot4QPqd3Zyv7YrugslhyNJ/Qt1rb99JVkqMk5JjFoYcXpP4ni6ZbL67d5Y7mZ+ULyrXRfLRMlaF1w/Mrl+45HilD3xraG3Y6ecq5+8eowlpdnGNLdYxFoCSVdCFMQkYeO0lt1V5zt7vIqO41L47qz5bt3GJ3h8CrWPjaOS1OWtl6bRpO3dWuoLj8DFtCrwesu8mBvAeYecOFB3X0HCK15feGVVESX/fT/EbUBUwvlYRPfLWbG7IzzArql90NNUoZ+juLvHijBvISRoyssLONzyn8YDB3JNcpvTAPzEHUtrbZryGk8p54m1AcxegJ509858MNq4P0vreGspxdZci7kgazrKujGeZOS3Eg0JWqE+9GjYO1UNvSHHDMh1Uei1UNFuAqDVtbFK0njarXLTNPrpMEvQAlbEU5Fok5PVARvCwN5E5MvEMQoytvkmAxC2uulRfFk2CD8dKc9unsLM4+Ntbthn4VWDLwk6FE1U39QCl33pfmdvcE6oCFaVcLQ3l2haq2074FaZbN5QXh3OvoQmRArdTXl6e+RcLeaxW5NdMO0ov1vLWnW3Kc3xrrTqr5qLAh8v0n1igG2lqDdYDrWxKXo1ppdWmYTs/PwjwtEdGZugHJqaw7mX5vHbeX3JxNFKD3GIl49SDPa2n6tktvG069pT4XPoeA+OyLLbsHt9um+7sWaUfMduAYVUeZqNvrAZmowpCaduC/n9/M4/Q9pfVIv/sTruJUWz87owP5JPmYWb/LkXUdLRDWsaQSn7ktEHt759F7hzm4Shfqyx/NbQ2EyWO9u/o3HXMRcVYP9lHQl7IWSXJL4m27053aRWlXRhsMIhK11pRM6c7rwUfrTDEu5enO2t8t6lkDpsp0o90uXxk9bGm3mNzRTU6C9XTWh+qBlAAPTk/wwLwHg8FsSYX3e5vuHvMimEX5vYLKr/qrcvC4tijN5+CitPZJsanEywQLG32oZu8FiXCgF9cGn59CPu0uE3xWKA8OrbEeDaarhSVf/YF+MtcefX/G9jVdYJoqsH50Q+FndrpF12gOJVrf6Ww6PZXg9RI14h9Gs+5ETn2qDkXtLH+AMt9uQ6SOVEhFdqlcw93f87FBs5FXlA9BNONxGlZTEenmMRjCNtKFm8vSHktFUUu0vHO9oU5zXd5fmQNG4/mWf4xNAe75qXhftXI4fAebuv14A++PdJuqK0oj5sd9hok38DH/FOoNllE+nBHeP9wF294M+GexUYldMe1ZHbVgWJkPq8HUu9rd/GVrq4Iqd8/R6GzlOxvPfif1keTpmeXmryfzRW5XY/P9qlyATvSejPAJANc5FZNtenQaEr+ypymRMgGaTBJlH00jE493Sog2APfOscwepZjPh5o3iWm1UZWvSwkuJ6sNW8IbHKShfoB1DSf3PtOxtr/fFgSvwSZgl+XMmWC12mfa3Y8nzNFK75mCkf9utfB9WFHbkdUynN1O48otUH2p7OuzXs0OWCzmt4n+Pt7GmHyifHNPqa6uN/Rsg88KNj164bPt8dl6p3piNzvMDeL8VI620tFuWrhhAiFDyvNlFGkx0XE0MBOXqZYeQF5dQgPFRXuWLgtj00RbWo9kwuxMoKDV4onO7mfstzOUZMC0DmmP5cYqBYNT36bLJ5GcIZdSEJM4ncSg2qhYB1veDGTcekbPhWz64/ud6NRIYTLXemCSfYBGqCjVLj9Svri5GqFyG6hWca2WeQQi4ADAaBmHK0y0tYOgm/jTCVIXO85Nz948PUuvYaVS/YKKV5RjrNqialM0UWUhTRlgrNpEWRTEZ2z+fqjWQN9VUmrD6N09j2oxJdp4jrSLFi7Dz9QoP5B5ON8VfqFkm8oyn/v7eb4hsbOc2XNY26rLLLAGlNubwamoumrktROVZJRRHcubpXOht4rjRVGM5clBnpqdTfFjyMbx4ikfk9w4XnwIFuKx7XHWyqPpMOwgU0f0i6xoemczND/p8UCYjpxQbqJ+XvcY9U9s+LZoOv1oQfAmNwFTb+GTm4vV6mGO41vh3ld5VuL92/lkOQw/2odahh+bZr+3Zvuyt3L/rEMjt0Rfya5BfFG6Yiqaa5Lx34dEenjytEke8+qMbT4j22BqFi8nNxc/RFMaUImupLdsYDvR3lm45gU4gper6Ch1s1p6YGyP8/Q8GQIWVsqFSObcrMZlqjgiyH3DxG9fZ0PDmTj3gbD4ypTp0CP6iqfN5vFDXz2H5k1p1eNLLbvhfuZsn1tC06nJLEZjW4EML9RTkpfPPXgWkTt6Yzo6Ev1BVuLn5dZPScYTX7v5mXqLR2OCDqI3G5B/8efcC1dnYTefT7Y/kpOzaI5GG3ykxUbeupidYLvSO+7m88KbOZItmAAB40S5+Mc6PevtNb1Phzlq1VR45SLUPsbKzZLPbDCoHY6GaqnLsVrst9iUAB/kyQ6tPc0c+1hUR27dj+Rt0FZMR7Npb6lo3jUT+7UkFOiPSJ2L3qNV+lNlkxXC8GIShaQBu8GflFFuPTY0F661JIqtUwKA0LiXDWaBu08y/CS3Iigu8mbZB4DXnjJnHCo4RYL8yD7E2th6HlYgwmsfFMtwO0Ugz6GPAMEa5L28YHFaNd0jez3nUKYMjaqJXa+Pg+ZekgvYKkpkflbeETlXfzQd4LNCUunqb7Aq4rDEX8fUNKK8eJbQbO8K7dXYePYmuQo2aUOsI0GGQulHWJpDzVh1plIT6b+z9ybryiJBu+gt2aDiMOn73gZmiogIKIoIePUnIzNZVfXv/8zOs0dnDeqp9S0gu8iIN/rX51cKq51umA/r7eCtcfU11q9AJiBZuSoBSfxFPZUjTuRJkCG6W6nV7xymWPMZLPF0129ZN8B1Xdc0qmyHB8AzKLqSQ65lDudtHKQnXlaykmzVy6AJ7b6Nmc6+kQdzF+5d45bOZw+sE/GfwfDL047EjF7Yql7GN9LNh/HG0jAVXmesxYCreidX1udzc5uU1A84Pum5XbK2fAqGm/chAkWzTeUTP+5QeS1gB5TIJtzeFDpvdkNGgqWNLTT34/InQ+6rWipIIlxNF7HuAlxE2L1usxoYfRCAofPrbJMeUsxWSmKcLjTieTeyWm0wj7lrHBDGGdpsrTSigXG6MCf4x4kMNnr28INdj/Uk5EGkwQM1hnXsW7AO7yZegF5gmdvLwD90BWtjUn6Nyn35bGbyf7/m/n1NQW4zM8Kc//yhlLmdEtp/yuuLoEQ5aCimjnXaq9oJ5CxKyiXc9yzv/XL1C3QkFt5uVJv+TM9iG9H6Xy+530TaJn72yVLOBXRIejMMd2uiKbrNnVYAFa9RbobZcN8kxU5CQtTA7+6iw0omlpDghWbcYG5mUe4GneGLyH7OMV9c69m413/FYw3jVn/j5ptIB6kZigMWDEJeouHc7XPXn8Yu2NhHGAtiFLHsNIJPisWCNXv5C5QSqbeuad7Rzri46GZstO+3jvIYhbamievoIDUeZv7Ahv79RT2011CzJFK/VaUMuh1mWioSOyqTbtWh0E6BHCh4rLn6kZfQb9F8cIvZvH4QHCfNmcR9BmMqFJeFc73aBeaBJWaWznHn8+ZMCald65ECohF8YcY44cMPS+MGM8sbLL95xCnAKbIfuyNy+kC7x9b+np0DWc1DkzSMGnKw8M4ScouVMNdOiRIo/dPf66qCRH/u5EtrUIZ3HxgXGvW166kWkUuYzAxvx2M0Ywp4nb9d6iTaMZE+7opGqZT0u054KzmxlMQU6VY9nAcM68vS+MqgXUv9MacK6/ojGL5iELpRjr/UV5Ijzc4JHmSnDSHFAMEqrJufP6Nu2KxWJd4w/kZR7rHZ5c8dB7tO6pjRr/IPVxT3jwuwV4gPOUugkkKNCKgLt2YcuprLjeTz23xpYj3+Kb66YTxVl6bgd8Cfxb8ZXI1eNtQd1krUI5y1Nhj4rE04aymfT2O+u12uR52BZ2J+H3Eejx2FVHdaL0VEw35TBHMRY+jNT6e1I4V7WRzrXa5mVOsamTa5chLDl+m+PKL4CvMfW8Gu0w/eDyg0D4HflCdJ6dFVVOeFvzvcNMzPL5vC/9uVL5tf2rWlbpQb0Rfzc40xv2gVB78O+UROAB3HjFeGh0V3vn+ivKZrORyR6Dgl5fkieA5gT8QjhBS89dXj0dYzjHVUdIBRa5+n9YtU2qOpVDANphUPsYmrGqOH1I3kOgqlxllawM+wQkkugGdgFBglnahHGFhW+L7yMZyChkymHa668wtztw0+gYcF8TxcYevtnfKC0KaV0XVTWdTegIIf8CIJfVXig32grdNU3A+iveW7yc7j4W3F4B64N6SKGcy+3FEUnN6pB3W0S78sh7ToZ0T27++iYjS3dRXRoklbypON3tbTR4k5AsZs1yPEKDkXEezrik033743wEW5wn8io1i6faVTOwVG5QzXJE5zO7xu4OVT8M3iFVmu6cqRQWWOVHJcurLfdQQRUj88kKHZDaFpaaJpfE6NhVm9jnQDuLD1sbXHAygqtGlugW5sXSxYOOBxOz/G4K6/jSnVc3VhQpz9QW5Eucf6pCzIQc3x90dzxCCK2Fi0GNFKuSHzVDz9wHjDcUv9GcvKB5mTyOZk1dzzOWuPLVii0lCHnJDzI1/2SwjdtoVAldnq8nTVktxjia+efBm6QZsOT7jWcMIBlQ4ry/Rz0bc1LB0g4r1SUGP1cvCECo4F3THw0wqS6loK6I1iJl+lDvwsvcjo2I2K4ZePmfDG0mgmQdUwE59JXJA4WKRNd+EcEW9Wgvl3Lz9FjOjfVBe/PYzdHQWkBqxo/K0gTjm6AoHbt9JGIfG1l4LdGY1GLsXZeH7Md5BlcodL/lDn7spPgQgEeqOB2BR004QcbhPooHG4E8JrHgPG1BE7Jalfi7tbDnUKeGl+X657wmPl6ka0ylKnuf6+wFnTc5xRChsSJSCmZUGrOmu3NNxZL0B6L7CA8faAhBet0hvatLaqrsO96lHQxx78fli+dpidHhivzukKJanz/RLqtyPnri6kK9jnDnlrXEKSyyjSxhQywtiEPedJWHwtMe57kRK9O2lF1yaMKQa+6vudy0g7DeUTI1CyLHH3ozXT8tvYGqaDQTjRoagTDQXCZUPoBQty9iVMyxXi/D7GSP9yX/4GdTDbULE3DSMtNFFF0Ou+qhwwf0XuTxiGcPd7Ugu0yKhPMTmwdpiGKoGy2cbjTrgdaQyqj0xmHd2fQ1m6He+X4wFznI36rFB83EDcOObOK4q2JfOzEvHdqwQMITIdKEUXljGpQS343+dOVlSuwteov2+U77EAbl2Z83wVNYSLScl/vnN/GBgL9hbJdBJ5lWEEvpiVT01SxR5zmL56XYodrffMbOXjO8NLN7fgLKrwVme6uxeEqDsiUqFqpJxH3G1muih2sWz0UBksnFNPtgDbSr4jYs2jXDmaaSxvQJgGljBfOZJfg777aW3y72fH7lzKu4ZI18w85Zecct2xZrX5rJ9ywOfBIQGfHJ55T6zsJrPj+B8MLML7efHbPTGyxXiRkjtEI/9YVrF3nsNOpKLwFsNbghHEhfZ82FF7lng/w7I/TQwiXG5mPQEvk81/Z50UQQhauDGpLLg1f9oQO6LWDiwnePGBekae0Pa7nk5fLI6UuqXkXWAtXsX/rCPpQJUctGE236c6PGBVAV5VjhzQ7EWKskSmLyC0sL+xw8cm1g0hxCSlmUkLnelJpnaKswZQM7mLwuZ0IEM4BSsyi/V+DOuhFBT4Mw/5ztyWpLoRSm8UXyDv01+wVp6iwaOZGCLzOKDnHiNRYQ85NijN9+6DZhMyvIikDYTyIWEUduu02d8/9oPE7AoVex9t3yrMh0PFhVUlbX9s14+VH+5O3Xj+4Hd6QZ6yCuI6pLyoS7nqqYhfmBOY6Qw5lN91/IO6aoKoshzEbmmEci/0Ft5j0yi8ObmjTUWxOeq8jQ59gN7U4r8nfxWY90Xw3yXwjcDEdJoXCvVCIS25/VXNMaJy5+sm3h9yV2pGt7my+BiQr6GfXYNscxFN+/3aYJ3IRoFLa8/js6K5X75Ozwo8ORiAlP8dS2rHooL4MntG7xmyp/ORWnDz8nET+RKyr49nXmEuCm8r0JqA1eoxUem9acfgC9NhMOcqQl//+ztDT9Ua0+ROXj0xt3HPIc0ybxHz/cCxCzcsQYb9/7Eb4BPRr00I0lqXRpXl1nZzIqnR3ZhbhHMLq3++JUWMqqTzRBX4O2a7onUlFpOVePoxxAO9OyK+Oz77R4xdF5Qj7Mzkh6/8Lsz/57qmH6x1MpkN/3/aY3kpFo8txC4Ikf5Ph2hxkY70VDN2qtNPbjEkOv3o4ozZRf7m80H/H76jC5fvXwttccfwIL77h9X0AV8u/+/O6X99R8hdjqL6ilkN4MeUZw8U79ekhqOJFbL/l/NIjpYpmsfX33nLfzp0HfQTjUn5e6j+Z3fw5K9Lt7lPWHX4duKR+Of5i6klorfivz0lthdJuFsD5anDP8+jz10CWxzJDF0xKwL8FB/Znc10AgZigfk41f0JE9HtB95lfdWyahS9OKM2e/zc8d/H97eh8rsQVMIRc4PZDXt5c4ojA8sq0V3vnqL43ZGaT75R/s1bDVOYtzbNm8U05T747BUSTV0/JI5Erm5s1oPTue9hjzi9SL2cxCG3f3Tk7Ml+Ju19TwCfzmQfm2Mg38xtmP+Hw40wr5ie1b272bTSCToyi3qVdrMTaF6zfBP5JPFSyPfTGs1VusfnqdkfPO7/5Nl4Pdy2rexmXmF+1iYDpjg5+w8XlUl0pX1T2/0AUQZpkfxvsicnsodGEGIVLviTZ8RFpOky/trjgDEh2TRkNyWjtq7y/d0ZqBrryFMO+mXCHO5POu1mqJeUNKScqx1Z1bSFfo1qyNlZvLTTSRaFY0dW/FTGCe+tKwyPruBtkYr+Ri07yGM2Diz2Fr+4NSuzvxgvkImidhF3jAMzHdLCh1FishUg/94bS39XERyK5ZVVUizXvsusXukf0sjrVdDc63/J/DeHb4MuPMRFUljEsiv+gzWbE8ofJGgIjy0LSxq1Ht9YZ5fFBXNMS1RAbOpY65mbE2phlcid4rhbtXhuToKxrQG+oxcMi6Uts4yO7zDME3n1xefhJox7tCJdH8ZpH4rfrxgBSMKDxGygZz2NH2C5rwnkRj2K+rlUnkQetkweKuqvrYd0bxq/gODLbtY/dle5Dv43fJmenLIhlorMWOeXcvOCik8mi5/xvQfBlS1WO5S7TqNKYFY3Gn0l+K7M0PNrBrNyUyxPPfnj2yNFNIpNM8qe2oDxZZHy+O+cUhK5mEnZP1rATLq5OcZ52oJ5HsHrYzJNC/jp7D/f2a2gkCcfzN1mj4hvVKeUbwRfC2smYC0dY3xnnZtm9KqSrWjc5pXZG2/Bd5O8ZiLVVqTNWgzzVJx4N880ucsDY+LU6GVMfel1N0drg9bAKCe9vp9jHlVfiYbTCJBAnXM7rEQydJMjZrPQjEH0u7JbLOR1hb+30GXPvKIONMdeliddO1cEfbRVLPu1c4BBp3iaEP3UcWf2FOWOlLDi3bkYEe/SgcSWGpHHrDWaMTNDP0wFZInOpckP8lG9Cfp+MzVYYLqzDBr6XpdlD/6g/jBy0UNelk9AIzKTSlmEOZD7YNhMXHBGLUhKzGxXtAtkaV9O6GY9gWY8ycs1iEzRhYgiGkkH3U88+lCB9zrrN5Fe6F0VisKH3H/8GWYV4EPMBo/gQ5U4cTWTE0miugtM+s9iWWB9+neKz80B+RJ3xzqO/CgIGtMnHiGBnakuSoj7NEe0Bd8cElwa4YHy4oz1xNZsDDciRZ6u8mwTKWmQnmR50l8NlaLEUm+vHH/P29uivuF19cplJjeSfCCRyoinfEMY7dgvixmHgSn6oc96S6/yns5H+R3gHVhvr1x5HSnMEoIpKacnWwwkRNI0wWbFvY+n+CreBOU12UBhJtTLSuxFajAkHV6eNwpJ7/IXagHcMauOcZa5KtufREOCQ5E8oQhn7PSP9PQFfn4z/ZviXvEZ6oVaMA9v9H/Yrn6Y5sr3Pb4LHFQUyf3wRDNoZTApEoq7evwrCIHisBgrRX/cPudLi8SDTFZLa3gOY2JBjQqtet8WZSAnF7O0w4illDK7qf/btvOTEJirpd5foZZcenuW9fVOFE9dVCZLYmaIKOD6FNVWVgliMNmOqIcZ66LXpr+Xa8zpJcF7t0e1G0gm7GNaVwk7/hjtaL6AKr2ztWo0hz+Os1Kpl7g07JsBVl0sjT1xVumG/KB18PWRdTl7bgSsHVhGAvIQrJ/G6SxY4wPkviHu2HwyaTD9wu0GdBCIz+rmOJP1bLJlu3vNfevDDcvQbF+ZuUYcGShqZWazBdMqXusP6k7+ZBtT7fV2qMdQvX5VF9YnC+FkBXZPC/kscgsIknxqK7z19xpqCuBNZFQrhur3U2uB+g0byd6BVVbfv/Fz6o3mdP/ZUz+NiRID33pZuvzF3SQ6xXLy3WZ0sPS2p+C3LzeRvIbGrGsr3OXPhGLPbFqldQJGcyAW+0tzI6Uvj3HjeettvjzRSMGRRfVhDXI8V/6z3NpObSAZzd10rberEovvL9FXbYZ+3WiJ/24QD7r4voSyIN4JQpTKhN2xJXDPuxliqjI/tjsr9JtlXAWXWDzN6TvhssSPWZ/mtq59lJtvbqaru+G9GSJ6oik7UbDrP+T7C2jMFb7uZvwJPZs9x2aPNfgL9U2J2pb5Wz0D6D+RD5j+HcGk8qtnXsRXj0cvwTDpC/u0mM0P0SeHGIBnLjIrJNbZSiOCKnXlHHPT/S5IzaeyJPWHnLXFLIeXPb7fc3m3iZx0xLN9eXWfv9YZpvAVEbsjk3NPZFCLaFUaZdUA5l99Umem3eQr5tJNReOo/vxvA/CD7+uGIZ4iIQminrf1plo7+ubx2/aQhoiRMMqf88fjduOsl6WgSI46P4hn7Ut8W4HwyeNmG9x+WGrsDpI6QGQ4d8E8SRRpPowwHj4L8ENiMGg9qtXjWRjpkDy2UXnaByp/BQOxtBKe5fUJUc9CvuqpfBhqsPQ7dQC1Epb+Cx3XKuxw7KR0AXZyN31Mxu9mfcDAXdULmoUYfRVTzM9N8+qMDORyrsn0k0JhQBZeue2bfid9ht2ugTLwj+3MXuypNYKPKIxTDcyL0BivNrIQi5LZNbPKxc94P+LN8wWmA3Abcwc5659jU8i99PSWxCdnovn+qDYkCk3LKUfUfxa+mervhZmC9uqgHp0h8e8+Fi6sQGHHNMr7rIqUc6KfjaDo++0+qnsBXflZFbsitT/rI6UOW8IYZZ9zLtxo4vg7rTDSvAu9Vh4g3yGe+LadPMhu4Q3ysbYjzeavUpacQYL6XPZ/v6k7yvHW5dU9FWNM5YF1OF833stOchaTwP1F3qyOMh7Mg8qg2TInLTg6ux0TzpYMICgNEqaE/MZk1cC37XaoUYcl/ixfdP7dvoX03sgDO6NH44d2r9dv6BmjrleCncdLyCkaaSoHHmLap7kSKZdE3+B9EjpM3a3SdT5GJSG9vSrVKIX8izmejczOzy20eZfquL9RnUcSXKbp3VfJhb/b+xSDjli6pLmaqCQTnO1NxOKddGe/v4l2FXcIS2Tre1eL4N6SQDA8DsM2Q7eQBP0nw7kvxM1RF8rkOuYkmGTyOnKb0y43ZA7q3hq5jc7G/l1CmS4yjqgzLdKuoa78wg6Tn+gLuctDvvh93OeVyr1OTfFoScTb0LJa7HZ4er1vTvqyZYhpkvFZC+1v3otB7ZO59VZO+aM+Xl8r0R5hHKdo59FRl8ddXP6dMdUTbfAe6z/jtTG6aAF1pOanEM9vKe33KYb7hHAFKhHuzu9VLVfL8I4KHj+/hee93Xu9PB1Wcm4ErX+9QY0ueUQsEgjSbN3dFRL/9deYlDayULAa9N5mNvKhpdwtFp62HtRne57e0EHcQp51dDSfguiNnFK1cHtP7PbWukgy2rQmyAPvXMS3HXrby5fUD/znrkPxhIISps401qa1vwh/vcSzPvLQSeEzVB3nSankUQvC6DIKgBmLuxx8nsGo+CIPXQ+E2SMzW033pGDPHdKcRCv+zea+t3p5e+eXsWbs59n4lJA16t445He9Jz56o6X2jVh585jvDXVzmXcjj9nycDmcsx9wijzZEjSqYr5BsJP6GMaZpK6VQ2hjaYv1sQO+jeKj3akkfozpdOJV5p3CW3xevzOC5zwN87MFZGtwtqgK7Z26ZcuBxRxm3yEo3wLEBEn3t7fGms0eooLsnpy2yeKyIn17Fe/GIYCKIu0TUxTw+9DSH2j0ysOBFH81/qLZ7O3dXlqP5LLsRvyn5fxMqg/qmObMkM00ot6MWoTcKU0N221jpvD0cXNd5716reIfo16psWP6ZQOconzSDu74fl1A6zj+9h5fPtWge3Wv+FURV4fj0LgcSRAiTLrGporym3LORP2MvNVrG6fieRXkgwJCDLTnWmT+NPcxhjNL3Uz7LG5T4XBtqXYv2OVI49Uu4qyM3SirHscNjT55rR9hijUzPejmn8sXaiCrosjsjPXpi8ni1laqVSsYcNvL7OmhLffq02KVYWmOZ3pj3Cn6NHju0Sc9plDYCK93FTt9R6xJRs7sEULD8x+xaVG08AW7KrKhjos3SLzkP88hgyRFjbyOQbsNHabEemg8IGiT00hmPzthTnyWkOaO9RA83+b0gy7IQaHZ/sy7vmLSZ4T2ZJCK+tVg6Pet9Ph3WeBblGBlxxa0XyaY59P51v5JY7X83ZyHq6+J/xeFu0DYE6ORMPjxsTmYfZdgFehkLsBKASmSu1h8XzcjFZI0ShmVbpKh4nivHssUX5cekrK8i2aVP9KmJhxoCWRqjhIw0+4f3V4Viygn9RWSWMgF9dQoZdTH9dbmN+RmUf21b7idHLn1lVQ7Wvgoln+QV+7wbxuwjFCS6K0Xs6dyhqsI8k9/QBPqGgVF1Narq/Grjnn+WEHu8pv2a2YWfLGFui9w/QJRDM/ly/ydO/zE3fYxOw/qQzHlCTnUkmStZ6UuGtn+e9jseqyrn9q38/0Z+sW0RdnDspFb+KQPD0C2HVvDGhN++egqVSistlsvk1TAyJNWXuHGAGLPC4VWJP6xeiYA2lxMe8WltpphJ6imOJ7zbSHrp+t/Iti1LQhUWUv2c3eFv6urq0i6Pm9jvcLip9jnROuAiG6W3Xf8BfsUmnWUfl7Wa3WefH52gqW92NbcMfTX+1jXKbbxhOWUHfHbQSOiZtym/RPjkAPmhI9wt97FutXVr3ff9iQ3AlbExvHwOO1uGJvejz7Gtav3MlqbB6x68A93n4nFvqGl8Z10yg34pH0dv2xvn66xOBI/1TJbPyD94fd9paR4asyp5FYwez0vLo5hsCpj03D3EFnqolK6fiFLYouZjtHVxYuraKbst6UxKLm5fb2gFY3wyG/14rG6ehFvIulznHcYxhd2VRNbJJwGw2FIutpwFvvucDm2OerFzcdqoMaGHBtGN/7D2f71xkdr1pvyaRh1/x0P+wSaV39CLFMtqOP1lb/jVxjsGaUvrWU6fRHAHnQyRHd6GGUo80QiwaVhhBFiWVKMAZS/mijy/YrflqFhCtRk4Olugt87bLjYz9ZNn/ofyIqvetoPj3JqKbf3PwmyiYIPnKSdi/2Qba+3n4keM5LRxtFIdbyk2g02Zbmurfd13DRbyPOoIeP/WIcYpeQK1HK0aWbMfcqMkd03HEJ+CEsxTNTSFlOxfpuc953LWF8ETQO+zSsFtVdKIynMU+ft6lLuLufP9+ajt+IkH/5iXKtULIvgCJ18CP900cBsRFzlHKKLcDF9wyptZ16FKp61kxSXw5x7+hE/HnkSFZ5JLCPRWBW6r/HrCHWNb/ZCDTVcg6R5t2tJDJb0BtvAwzWqcUr5bJ94s/msQUd+bWafBeQ3/TaHtfXoE31258o7vyDdyiaPmZa/vqtQ/v6ezXqJ4WkveJ+gEayPc1H9eC0tVQNyC3IxnTJvDDijSDMfgvdTFgzfcXrZxb1vXJfXbG8fthLdKdbzV/LsJCXVvpRljKUJVmSg3j+W4sdcm485yE1uiqTVirEpnoc02IXf7rxs67WX+FKezeP7iAUUJ/DdUbfJat2/TMa1IngSwbzr3eX6OWGmhc9oyPPwW7cdrb+9vbHqID8oMVqSjiAxKbI6u+VhbvEYccYZ7FL17GqzXlF6l/7mdZgnznwF+YcDvkJfdEwui8x41G8ssCISCYLEjMUyo2qJ9vJ746hYxTdgKFfXtkfIchzve0Ktx1ZnWVOXnxIATY1CHsauaeuuEqwITpD6yRL7dQIRffMunhUYI5riTc4w12wKk5bJ+sYsZ7JYj1lrfTBJbixVMPL42efcRasZlwwnadJjdA9CWrmHmF3auikHS9uh2XY7uq2upBwxew1+ripgFVuf3bjyXRKc/2YxU8htx6pH2RDgmZshzNygpyPkZ3Y6B/Q56pli3PE9CF4nOHn8nNzQFYq9xVb4dpwUPYpZtPD0OvBRv4lPfsI9EcD8WqWZqe8pN3Txzk0RYMYCon5Db753Xxhv+3Lo8yNeG3xx3pZM8hzOIabs7bsdDFvqIS+/3eQqzeh+UmxoCYd3aYlQrGaezbtFgmH0Fiq1Xat6Of+p4s3N4pBWw1wzPasXDxdDq0NEGqZ9LvuZnRvcC9MRxKFGlvEM3n1irgSS+SiolIOYwwXrz8swXe9zNV3ZcSzjy0qkTRdzgSH9jpSa0II7ZZ4GRXFeVP68Pu/ieDE+VYRWm8poq9YJezX2rtfzfv3o+qjp+hn/m+0j5cTyVlHZ0fWhANZXDLP6kCRYn0BKr673WEqc4GRfZ3qyZ32qB/WZPbpAEB7Foh+j42v8lMj9oVMerTIO01S46wi3ZvXBbVabLJ4nRx2Kx+xy4ZqEyzThq9zM3RnelVNwzn3jaw8pjd04Nw3HX7015chBg7Wc1besAquP3/xin2lGFksO5ilJtDQTrAs6maUA37MJb1XFiHWCJ4mH+HCwHvR5N67QimqlHTduW45BI/ZSvoUDEmSN4DUIFaLVEJVPMO9m+Nodeut+MZLX97HqlRjMMdn+cG6sUvu8euXFQcw85hyHjiGGT9tX+J6sMB/cdtVT7YXX7nLsTiudj/3UXC/t3ZzKvLPMUNLnZDZFnQWmuC/Oe3wIpMmsi8StD7WEx2xr4vk7xxOvwooOv9Eh9Y+c047UJvP4Bh2+y++rNkGfeHx/FyJvDfuzqI4HYXtuXq/UKk8k2/YoBxRBzCFhbjHGu7DVki/Ujn/n8RZriCLREK10yJ+Bw6P0RE7fYKX6HfxROP2q2Dzx6WNOt/7mUY9AayuOeJl1pr6FxR5LVEulHu6a2eAWJp5it44eIqZ4AB6v8bo9oIEHCQD3n+gRZ7DzQQUOM1bP9tjQjgu/czRAUOe18mnWcPh9b+/nywkzuDvaGlF1vce9dPXOL6tLIFL2ygvqFzK+Xv1tS+v7abuJ4t8pPqbncu02G10ov7+V4oEyEmMqhGqiUe7SmocbrqAVZ1yoDYjvbRmYfXoGGlSMrBbN5e+4KMdfeE9tYfkDDwmKTYY6LnUyf2XglA9b4ZCJz3PtWktJ3t765TmFrro6qV+NR6krYtG/gA2ma0sFZMljWJuPthYiYQGIiBurdmEmEa2RmlcK8eBL8etyiKrzxdhiYPY+PkBdSYValJdj687ueMdOPt7HBVifXHY7jtPtaOZnb2tjbmPO1wfM0bbZOzlAhwX3u3Dtan4XhduCZ7nrziUlx2iiI9aMt9HDfArqb++vjBXmWo9dTqBzvia6CGeu7O0L0+jKhrgKGX3pDRGw7o2vzLYj/UxCkOJHoPdLifykuvjH0SlPfos+y/DaD6S2i7XMWc2gLaCqxO+TWMFz6UN1a8xvWDXnsf4xj1VI+1zMI4VlwM9yluH7PavrUMsuGMleWuFzxJgfik7tUXdvttw+6V5Zf1yXAhQShHo/oGNFCHQOad1/PBJytbWTVybtj+hxfpl4Z/AdyV79Zc202Mtxvpa2hYDqzaWg1VCvmc56DsyhO8C8LO0qdmHe0Zr/LHPXme1DJbvH12vm7HcDRbcAi5klIr/ME+/4Xd8EL1R73+1hn3jiXyjVUr68fFCw/kVHwpZbUDrKXhlmKYdwiwHfedN/iI66cPa6utBfI2kHGh7tVJe/73JPJIq0HmWKPrJXTW7irAS0L8vGN3KCxPw1xRujTbTYCvO86rAsPcmnmdhe69eTq/Jp7umN1ps5Vi/nba93ucOtwqO56T6+2koJaENHfIg6X2YYWihfrdNPtAXIsp5Oef85f+xmI9+s9To8ppe0KBXeJmWaN3bJb3JZOi7np7m9XxEXqORPMTRbpUk+/cV413aMjsu6O/zyDvTEX7vMVeA57+6FmVDNKKQ+ZTTTzjrgf9xEUhgCL/q+Z0cvScUODWAXhNuA5yPcChlLNUQ1WyEd6Ep90r/zpcp3JF9mjxoQfkO0Vf4B9YHhNvjtuMcUKkj/Oml4gvmDctl+ra/JsoQKO7EyVurMx3stHODdw2N94vgNps98KUX4dr9ZFS6RRTGqgCmO7Tv/hLbYX8tm/DzepdZgkXVNhg55lSkFxe76uzALK0L+ArH6llBUwuXvhaxIjqJ0eRd85hZKJKzg47WE8WaX28oKKnrTPUP2jd2qfKOf9tfxJqBf6G9e79S9OPvLz0InOKi3sdGQYKrrYmOcgAUST7zEojqR7kREU71fzF5FSWDrnLfivVk+y6NtiPzrx9sNWIDQlYbTSofBS2zhqZaR6AL4OUiqLIkHLQ9JNSZZHlndTN8zZOeJtRwx9XhLWusoDO0ipKetigOLtDxb8rbs3fyEJ/DzlvM6nA031H9unHxrSZW0qYpIK42BdPLmpRoG55uwMvvrbPVwllXHiStDPtnSC3+cVGT2mUUuduDj4F+4GEZ5DmzD9RYfrIUtkKOLcnrVS1IDUndonpQt5dVxtwHRZxzEoJi/lqE0mrtdaxy9+XcGjmkBYzbrxUv4S5t1/HrXVMKOzDSl6ofxDGwi0sSn8Fxmx/NNxBqNvuAf1bp0N2sOr9Hb7fOtw833kXqkVdlkGl+soh3GAdtHFlCZVXqiv4o2Vn+WTu3q6MyqmkPZSVcjO3F2w+FL5QmeOVvx/Wx0v5XFN3lg/8rD2s+CUHzcQ0gnOT1WwIY87oVxgz2XyqZ8tHRzLXq+ggrcNEn0O15/uPlaq7ALt5onOT/+JV1cT5tBFTFBfZ1fc7z6H1u9Na3eOO47117fbrP+clrJfQWIQfttL1eMmbyhQK/zB19/vSblBlWnYPv+rLT95uHCzldYOQWM+i4TqJK91Pse6iXKYBCsO2/YaFpfomXixcLIPUjfYOlE0bs+hNAhwxAvJoJaJFypcn6XiGJ55FXt5Z+9hquy007ndShpDPWXqY4m3GnWgoRMc5Eha+Ei/95H/dawPq6jP+eC6niPxyv75tn2X3dRtEt2F5+b/Vr6klPLpWuaXDrFqp6vzjhJ+XnhXi+QqaEV6Uq9KDlU6uLCyPoZUkxGnzSnl7Gfu6T1yD5/5HLcF2pqPRaPdXiIOE/3LjsQ3vplps/qlBTWfC5ollrvFtBvxE5mJilvWs6K99DinW6EH3RM0TRvJR8L47jqXq5BcouRzTKRYy84oJV17oyn4tlbaf/ydEP+esXbrH7rtXSxPe0Q99c8u25AMrds7Y/wXxVbw5+hzZ7oRNJ+sH51+aS3s7u//5Lt4cFn7k1vMY4I96tzijxYq8Js0rZQQU3vR3ay5VZOlgesRnPzZwm0sE3d9v7gMru1bqkExHv8YhTVMao3WbREXFlGBw23PVN5oxWY00u+qH5mf4GoDwhjOP+AfrS7OT8g6VrPX3HzU6kV3WS8rU6O++369+rOuXZOk2MXv4LbSoNaiOkiMY7t5TfnpcVVBWsg8f2hhnl5/qr8cuMV6wLZD0X7szRiJQj8nOHdfP1WFy/nL2CQ23pKsJDOm8bkAQQdc9CxxHqq+hOG7/el2ZTvwEJ3gPpzY/OAG3APXXcfH0dXMzEla9zJC9YN+kboi8+0M2Lh3zbs2LLMrlqqKTo+H9Cx3NMX28v981ut+MOjzxzBq7b8UrHVtVGRStYM/TR//LaF7nfbUV3Yh6fHZ/yjTLPrt7ku023aLa4k1lW9V7yrAYvdI/+cQyXi3UDr+ZqlwyoxL9oF8LyVj4Suf41HcLjdx+Abj9HqCxfxC0aY6LfQIAN+GUNiphLkPGqdebmIatD5SN6wXf9R3PWw2dfhEn8z6PL6McvCLTn1Sh4iJW3hIn4/vLddIvta8hfpx3HAuKACiWgLd3uQuptWfkjhoH++a4gPvPdaGaAqz7v1qJbXulhr5fg14M1NRvzW5+5xHYGhqneDT9/e5jdmI+00pCxJh/ROKK7NF/Nn6omXhDmz7fjEs65bmdPsdVLl/hFs+aJ0ufJ57zV3e16/C4p8GUvVaRTA2rMSr33ho/H17QUwTEk63CIguxWNMXfL+f4opqs91N0X1ptwie8cXFVtqFlPkStU+rbbcgOeprWpaoYNmGGwiB3O2K5UkLoEOz1fv/M4AvWbPYwKPjmIY/CJL0DizIEqI5z7ccstyQZG0aZf5rSTypeiM4z9X2q74OxyPnss5kCZxAQBHhOLYmb+EdNoZB7N8Vz6mvc/D8h2WYfny+dI8GWuMg2Wk7rTq8oB1u12oM0sOFEzTlhSbOfkSfRhVv0e8bP9WdHTlwz0jD817IbzjvVyWLBsU6RDnynjXYXQ08C6VtUlmRElEo+AaEwb0gqeG/0lnGWrysfX+kudUaLHbz3tHgy0elsJvUd2t/FxCEglDacTIBpDIzRgS/eOdX+LtgImjUVYQ+aQinlFYkSEF7lCw6Kd8yJ5vTp5MxYQKWX+AqzjQZHV5Y5+K2KeLWQT/8h9jMAK4pyRSLtUZZz5b+sUsrekK9IYl8BjeNLoHpjPjp77RmPdrTLwXRH5D7ZbPbnuP1eVIBT1OjpEWon8UlNDFW65VhTbGasroulUs7fRqdIOG4K5F2PCneCPffmP1au3uxWjxjdIk2tiJGWezoGm8pD6rpGSXxkiiUuCsmwbeqNst7PSTWKi3c1blsnhJE8njvldj6neJJL1Z5yPga3Rp6Y8g1OLeb5SgtK7gx0WsXS/P2fEWx97M1ZRBkn72sMC9glZ8/j8qvPR5+mXuvFxJxaQF82ZOO+wlqS3tQHfhyJyp+tffImNoQrrQqZuA3zadSiOEKZ7fisj5YsLsT9O/XWINu+Zv+lLhrUbdhR7q+KPPiaiTXRz0sLGh47VT1km51+xihO9EM/2R/WaCMkdeh2Zx9tXoBla50knXZjmeP5mkWXY6Qvri7b8PAL1jyzPiWO1fc+GZpUEW0BHGvjTl+n/Utu7zxG6mHsRXo18Fhi9LQamuy6wRmuVNiE8PIJ2YxnUgsQ4YK9DrWs7jeVcyNcsWiK2WQyYKmp3cgPOH6OtE9oBZ6oaJ/YxD9QmShfDKeeYF30DmjdwYNrnDHlkvBxPg858XrAssC1UG4TUgIB64H17ekek73wP02rn5d87PX7HEazpnd30jjy98zdOPb2TwDgO+nsn/W1BNHm3gNbJLQbiTZ6X9WKPz1IV31bzwphsN3WSbucKzTfIVxVYmLSzSHcZqT3L5xA60bKz0FEhPHyR0jkgrzgdiIYYlxgPbbf2tlwunkIuoeB2I3bu3ha5qVsV8PLrflQFJMWKSGlSFQWJdfuKwRuU63Mu6X1CxyDkg4aATqF4ziMvqWa1Cxw9M5QrRAuQ640kidUcwlcGS+nQMgrgczbYZujIK0aPqnXfyxx3hMgtz66ku4hIDyeFGtbwV2TaLw2sSxXJIhfWmNu/zdo8jbS7FXBpKhliOWI+/ZbEsyeIzht8tM4072B+cd3vmAvIE/ZYm8rUGb1HnZXTLBqR9I9rx8OB0rhhzHT+9i/eK3LTzdbhZuPbZDnL6I43HnpypDd67qg/FCt6M/WCdLDY3K3yGTxRLwcXkH5mTiyPdsVR6WerBpZ+Y931w+rZx/h2at9pR/CHpz3RyydY61cyXp2Kimtdv+JCJhflDo4qi3V1C16Lj75ZkVva6SmE+0xfc6u/b1HUOgMLN3zgYlxJp+Fpd10WTRXrFomY7Hbj2MDcBH0ZHpJ/kIMz0Z4YP02IU6jq9Ymr8d/s5e5gk7xxVRwnTiZCb5E7sZC39U3FFOpb8zfWVjqCV+TxyDKCfBJNsFuDgFVX+OwPWNKLbHYLNrtcDAzSnfC4ivCOx6AgRQvlGlKEFLM+l4BkOrCqlgWRS7LgLU/+2b+s6BrEyPiONDbMw7vK3cnJwh/y87hi1Cvxd4ZWeihwBP24rysJrNJP5AN8JX0OO2rdQsspI9AB+UutLfi3zdaQyot2WKkkoxtdPX1GsuKuihMpBziFcgmDLKzT+SJvb5RKEYLqoT61aXqClFwWJDz2stsWLXTuhMYsidTJWGcniYC2tJxyXe/QD+S4XmOuEcB+375LDJoOtOINBsTTcxHpG/IzDlha5WjOhRr0UN2HNPdcZB5zERkjWP/vBpZ/GXyv/oylOtOykHTsRPxko+LFAWrPl9n43uwwf+b0Y+bM94NBqF/qNTBVUVOQVrjz86E+JLrl+CJKlo3OP9uArhaFFusQbkGcGZan/DJ/qb3AfY22bECmyLTq7VcP6R2wRQt6TIdaeuHL2MH7HFiNZdNbIKLLERE/DFKKc1tvlrP5QvUUfJO8wryNweMF6KkgM1P+7CP5Jhr3ki/wt1PWE9rTlz3DRT3W2kzMbfFrq8eMv5GeskJb6Ixe5BPwgWylz0KsERgSsWf4ysok1KeO5pQFlx4gmuCSDIqXgIiHIjuVWlo0eRoZn/Kvj+8elCNhVnblMDbIF+/b9bop3rQJTS/FE+2foX/mIcH4FiqRnxsrpWiYdna2znzYk+fyeoVlEdDxuMGI94RP9qX0ZXaYURmjjudiRRnMr4EOmpvyjrm7reYes+nvKL3nyrRjDQ9Rc/vE0OpKyDFgrQ7rZiN9G7IrGDmwrucLq8bo5BrdQ9Cj9pg+hE6+jeexKR424am5Mvuxrtbr5/p35sA6J83myBSX74++vR1nbyJjHaSxiCP03uSu6IvblU+7Nc9kVn8T/CE+Z2ByGkvqLbHobvgOqzKK8FvQ49mLxA5j0NWdalnXA7W4IC/M1ivwHTvH9WmFliX1OmyNcmqpreODEHsn5g2jXGIC7VjLa5lFAaKnOXb+CmwfOxWZ48Vke8sx2wjS3V+XcytikVQx3cUFl5YJePZMyqluLB4Jyzbot/vAM5G0F54J1JZfYEa4H7WW7p4oT9mzpHt4lpjLEJ9vuQVrN8SxwJ9E9ceyb4klE+QrluBYicKMG4wXfjeeyze9XyieuMaTcBd17WchcINeml+bdykxbiD+WaxtqYF+rw/o8gEyzpDLrGz6+f6MsS4ll/NENx9mkfeMjSXU0mlZZEnWx0SQq1PFZ1WakbjhKNO3Sr9AijNW2T9n2RssNhozd87CqEM8y/aWhp030x4bXtFh2RW03G9BJ1lO6ypJhBKsy387PpYsJ8prljI960ifsbMWGvAQlqu+AlSXI3Ud/jJ0nJ6bfLeSsIJefJgeHu5elnJd4UIvlbfvkvVY96f5tsRL/pn/FrvLC2PQ+2brOqTGP632MCW3e4UG5x4GiXF8lYKvb3nMs7GYPHxoZRjxOtWcuMpO8llfG6uMupevIalaVF0vrirqf8ofTMdG1vr+dr5bQ80gEne4yegtOBdq+574lSd8pnz5+wadAoOjK0KLTbhMhbSQyfC9zk858Ly/J3HVtR3nMta4stlz2f2I7Yq2B+Q0meXwi1kcHfSj6K92uYuUn4loJF0usMqsSBnv70tz7vxXIGBY7/Ehp7JM/G/M+hu2mEBLH/Gz3SHMwJY8pzf4yypsgq6q7nyRx/yLviu8k793ZfwulvY+pdxe/+OlbSnLZe/ik0L091pkzwhxHCDDHksXjuOH2Fx/zM6KnKKYY5Zl2Q/vlIv2Dz/HL30J31By75F1lpgengskL+C21K+GLMWSYHOkLA7WGplNQkdwILXuZ8lQRgNW0s+VZYNdBPAg3XuXY3OWZr5CJAW8kv2kteWHAVpuFyV4XNdcSVeSbycK0F41poDooqQF+GXBywieoYrt4JZlPAKGqo5guV8RHwySPvMy44yNQnkO8t7MsiDmKx7u87yTtboQWukA/RIbq0VZSPVrX/ujFFu+5POWhJsLRiAru+Bv9qVaYny0vfnCQi/eht7V5o0VVkCjxGLa8J3ek67WjrFxhKo4FsvoqK3/7o7wV0sbGna2TitamYcRoQvu83aZfofF/LLwTbWaz7bVgkOmpT6S0cpEPXR7YcjPD1/e5+f6F4tu70T8XqnNXvHVOYqipnvKM9WXv5hyDXwyQob4bedtXlURLvlArKLEXV+vJzy8IjM+o6jC73yNx3mllepgC5ljPg6kQQ14tmVaHsXTH4x/PyPHWfDzm1aeExl9MDg9fz/3ObXUQK1MVgkePQ+jIeiFXoBCPiy/K+58o3IjCQzK/ZQIVWWR29X2/r0uh6WXkNPIPd1g9TRe7acMs8B6W4nyjXz/+dNpx4bb6SdxJyLTZLSdyqFUh/dpc78f8NBe/zkmvgUbPzjLxbD43skjZjF1hEUn590ZTiI0ZvfLgwGja/y3o1ava6IioumrYGtevtfdGBXe3Qh0UV7a/vr6iOYOWZB0nfaSN8V6E68KbeeT+FzzU/gI2mEGEXERaKlJiYCb+jPn6uUTJNqXPO3gpwGaDQGeMFiM7z6tdN30tDsecm7GbHnW2guHd8UsQhMRV1NKb0bB6jepwvcw10pPGa9SNpDCXlTu9HHIlIygAO3r3SZxXuRpZmhEX5GeFUNEubOCef3Iik3h5Szlo/fgZhK116wNVgEiVgRnsVm43XgfDdHIav+52yy+6RDzx4FIYCzlWS9spI+v0/mzWuqH4P0R1fQxmoX+iQdu+/fsYXpWHuqUOy48dyOGDgaywfYaD0BKQRspp7u6cVjdqqZg0gjD9nqxCPh5AbSp+I+mvdJc8txlnRmRYe0+zhLOQkvOK+ET5RdlW5w75z4wCpZFczr3RNXearc8Wwu53Lpb6w3N04TjMyJl1cCffZtOUewNfS0fe3YSbZHROZlIPyy0ks/O2mcdi+BNKNML8QY1VxSQdwVdZ9T+xJNbwOTsrmiG7/W7MRPzrs+5AiuEn899dSFZi3iOxjTHvek0ELXicsXoev020ceHHF222j05jUtvzyUibac+n2h0Zx3qTXYa7oXx/fJhsHLx/dct7SBn3uc+o8+IDFsgKW2HvabVpHOlmr4i8JMKTZu+ZyHtNnmVW4pJIUDdqiDmaoQgrmWwWQ119Jxlmd45DX9MgzVJ0Jq39NagGcfOWsNy6nrWn5sCwz3wkmwvT35z3PSZtT/9zLVNO59JC52jG5UjpXVKGOX+vK9W7fUcOhgyRg1fxPf3Qc/c+Qbi+eaflxrQ9cdTH18knxbKruqGlR7I2dY2hs+VW6zfHFhRjwuB1vEJCtrLDumSfNYPeJuw+r7IR7/oV7fPV7cP5nYWKcX3og1uNdwlOkNDZnjH10+nz70zL6GfpOdLwn833p039kvr0r3XL7dzvpmqyUfXTl72ucVgkbyv36d6O+I7wJTKLyEIHa4qf9HS4HI8P9qLfODBxG0k3/g2e3ldVCxv2yvQ1mfznmfW4rRfanQ+psH9uzARpszv4S77//6Xv468fz+LiT9PP04Z/tUSolSR/P/v/N97BxxiKeEVlXlpumUl/0SvuOz8FM78a80HZeiu9fogzuv5CLkiAhjy82H21R4LF98lcTtfyoFylI/q6tD2XQQ2IhO1oFX95qQtLenojom33iRxkAZ6AMaRJXdfro3Cc3VXuBAhokYzSdtdSQvdQrsvtmAGkjOA6veX7QkOeaqSaSXA0qqwPlyeAOkt1eWC8G87aoB/21UJ5vQgWUur1e3xPPoUcnQQn3ZO1sgqe6hwWhxpHQEhqgGU8C4HZelQe98IBqnSY/5m4mO241EGjPVRKPdTmfOY16MHq4yk1u94U3C3a/C9eWKoC0gXmwdsXm3ve9Rls3p+18pgDn4bSS9YhUwdi9pvvNbvP40/91JmcMD2JfX6XSfDLxdWGnziWMzr1ZYjdp0r62Jrv1VDF/mC6xbyksMqB+yc906SYRMJhnS+Vu0nAba9VYmPtPTJmathcTp3Vu3FS/RODR0Pl1vra5bFw9UXV79DQjIbo2mUjzhb7iuZOG3yz3jSgWNJj2VW7y4nDG4X1/UMH/eXFErznRdVFfTqvei4Uv+ugNc67S4ABG7Btq/5/g5Kwbycy5qJ3118vxBs4FvsXQTvruaBMqiD8bG/9V4AOcE7w+93/2H9goi4c3L6OasNlONBjl6xihjxbHl6yFflfYPQwvWNu3HnRWuC1Wl77H/3TS+OKry+lbY8yaNlkqU07w6GAO9CK/brXccVcfErpDyCefvO0XO+nu5wIr89qPNaXGxrEt/D7pEYAuaoAm/IerkNyDvG0nM8T/c4Kd2CZz4jnhXHmj0HwizL9Tr05yRGef65bI6/Pf7vO7mA5Usg31vNlvERr+VRCRjkf0ObVJkX+jMNrTVnhQ/BBSAM71Y7gjdaMq/6Ognm5Rk0aWXzUd72QIWswbhC+VLrTQkdfEAIHk1MOHi64wMM6PXuGIOH5nzdx9+TdBl6IIXtuyX3HZOqrLy36n5Bbp0qvnnisbjvrqRuMPh1Q53Vn5jNFeU9x8onRE2dyiF3fkUGOX+iIPglyeqcafsjV+eHgNjB7ac0Ku2C1bDWiQVQFl7k/hn4pDJ/6Rk82bmQxfm+8hA4Vq8zHbYLBMLnSqkhFHPHJAnj++qaPuAGokwsi3ZIdFsDvZlNciXjqeJb7AnUjFFGrCPNm4wBWUL0E83fGB/y6Abs/WSMmoVYsDFk3idjKOjFxqjIGDY6oOl3UtNJ8M2S1NfYyzNEfhfHaSwHX07BV9j6PqwH/A7RsYPAQeR3E4hdpXVl950/K5a/y+cyxJjvfZWNQ0qo6vuLswgwF9c6g2hAZr270+jhw6gH4Fvu5+fjFcPD+Srw+gc1TAu7iO32xic7Ua72HKERl/x9J9GKp8jsCS8JjSei9jqupvEdwpdwQ7kwEdWJMorrRXTrywL5YoyVVoJHjiWr9Cbz5N7Z4pMehzuozKv31YE/qhirkaJCUjxhmUvh4kHEfN9TFO7qtHMI8uI7HgRx4JQhfyiYxo4fId9CD/atZ8W+BXZxFcqT5qFPv6bqzArpPSvyNaGlXxOKA9P08CP4a2aR0lVK5BVYJZRnwq+8dI6WhE992qc6FkYRPNl9XlIfsdBM3zzq+BtPic3Y+BujJmPYf2OEOvM3negY4zTGh3FvqOYAYwyIjqHLc/pNubBgDAw8p9/PMvsmOUajiNkY4zTGB8EYggoHjEjV+vyfAJ6rYGhYt5F8r6e0SgAfsbRUKyWAuGinni/n5+gyu8NGcg86IZFn3bjzaIFl2eMC1i74Yk5x+w6xOqQuXIYStaJNacKAy0C8BBgI4HslDRKafiecTkAHQrHSUqQekfws09M0DRtLS/GJp0AWv2baXW7pePEqWuc5mdxTXNHdMQsJdgsDXUrb7pMeoS6uRVEXchdrV7ApptD5tNjVsxzxt+0iYzSvU82I3SJZWDA+gxzWHRoDCJCsI/0QOIuYnm3FZE4rROeUmyk9EF3UyNh2aZBXdPE4aV/RoGC+Id4pP8F8caA8C4n8DchEgGkhcjduzN+wIJSlPempwN2IWaVgchOQS0+d3o2p7ChHvnYpTuxrbFew5q4TGuIq+jW8NKZ8wtvADCD+SmcWv1sRMi0ozsns2M4wdxHqDcx6fcGfuLwtxpP/QYUianvGgbE2L1CKEG28x/iRXcgsmNGEiHeDAEQk0DHwPJ7TGPjjWFZNY5QCa79tQAl9HyWm0HRbzEBXc3VQd/SQdEEWmbewQnjqoSAceq3c7yJevanfCJHYYf3GrB2hIQNGC6cTTqd9VPQav4wHYids9OyEUQonHGMWST4gChqlLvwIhw8dnHf0juo1G2MkYwjraYxwGkOjY3h/Y+jTGCGMwaHdRIZM68eKLFCqBx8hFOLbjFJ3ooG3Dws5StnPnHFN0yhz+YleeLJkT+qc8odceGJyUJ85u20vg9YUw7fK0AUT6Yz/5cbTYHM6gcQwYRHEEkEzCZADHKBCVT5p9JCNE1PrC7r2WdBgwHjE+okyi/FRvk4f0GMzfnkERZZa/QR2E9RhI5PewE69XLqh4QqJjLGgTinJQ9xkad1u1wrMUmD3D1M1s1IK3ENEYY5Ji262kEXMts5LA+zQXZbp/dvIHPOoCSMsFksXxOzl18kefoET1Z43dv+4B7MkxBDJhrdUWujsBq51Wl8GuQIRHcfSp7N2gG1OltFrj09PguA6arrXmA3f93i4TfKNydwYDZS6dOkGlGAWBzo3YbJhCnkGbFF+snVK6qTvqzqfy69JFoto7TMLoh0/8E1+/o2hNdMYDYxhT2PIkyzGYzxhjH4aI5ysMKGe5nIhBNMYGB8zK6WO5XqgT2PoGpNsujAD6jEK5lOx/+R9fgVWasjTGOM0xghj1Jgu2BjXaQwPxoiehT+NsZ3G4GGMbRHSMcR/jYHPMRdSxnnEXdBTmtihlWDY6C4wm9pq0smNQJNR3esxPV3TYKdrCmleFvkhpzIr156MiYK9BW/7REP/2JrxI4b+VPJpjmsmWbF8w9hIn2gLcz3EqtbO5LIUZ2xK4nKy94miKZwnerewNksjQ5C1g+f76XlQ3i8yNSNY94fzLPiT03Tn2w/NjdfC5Rb6C+sZzo7dV2e46yz+5L6Rb3Jz22Hl1yqkwimkrKaUI9ynWXhyirEh9NhmozNejaxxh5fzhxpzbTbtTFyFKFwbO8o3dEESGftbGPiE74XOeBbyGG9CUoG/ZcXTCecSx7wC0rPAEjwzGA/S0W361tpoc/kL20+/pTM8h2+Mief1Mphc+7tZWP59bnPZqjJrkRwC3ymZnovlFIuY7ZV7FqQQ1F2/zxvZbZ1yf1FRvQsoHaH+BqX2SQwN5obwYPB7657Jn87tneMk3jkslmfn3WGmih+Uhr8hImbp6RV8eonMMVmJ/izQkTzzxaRkI4m+zPg8Mks/kK1iothGZhFkRCtTfRVNY5RsDJFoMpHcI/EqXk4jiX7+szbmDgbTtVICjAzmsxOprq0xnV+t1zYy0abPi/jAcC8SxslFo0uZGLpFggQrHw40uit0kAf9cJfzaqRCu6mpZttjKjt2gRQYY4cV4PeTEy/e0m7B/6HPBp1H1sY4/TSuB5tv7kUxq+BZm3jOGRhja0VzSiRu3MOPWDLNyAiI1irIzmH5yP157v0Sue/cq7VKbmiM0LLhh/3zm8xuRJsI49Gm/RLq/oxBkiclSn/vse5sLTib9O5AM3pDpfBygBjuNAgUfYvCECU/AqxPnfCEeIB8t1uef6FXRCsb8//Zu10/mpT3x6MuOzMz+774YzsKdFy6Z4J+cJwFdIAdMXFnxUFv3MiT2ha0UpsjGv45I0kejlWewJyhi7Nr5Iu5s17ZzbfokH6ONXQMCJdCncq2ff6OLiHGODHojBv9VjZQN88cf5KAT/Z5pnX8dE0hp5jbwedz7wy1F7HcyMuG57VxEKAyPFgc5idrcSoHQl/2l1ZBd8uN05tod85HcPeGK3T7IptW57y2NOo+T5M2Xv34GkVorY6Gsb56yK5HjB7vJc8LL3vfniDW7s8iJVTPRcfx8+aLBUNxxSKt8XobUn0M2wQ7yjGc1xVPIp18h/XdEepREeRCxE8P0CZyVYEuYUBk3+Yck0ogOsM/uYyZ2WN3lK0xFmypEgzZzbY87LdZcpCy5GY9qRBMuZVkge39XViFWSiY+yVIxoBr+/h6FuCGxg2X84O1WFJqYlqYpI/iCSo6ObMSGXpRuJEPVSNmhImb1/Ikp9l7yH4n4gP0dGbEu9nUlqRhpR1LLu4GYeKKw6v4SuZKtD1tDjE+aAnW7DE+Wx8ep02RF1Zr7xe+2OCbPc4lOBEJALndpAVEgBno9h8LaAzBnBFGdScsbT8wuS4B1Lm3lsn5EGtBS0a5/o0C3lS/uN53Ru7i6RDT7HuV4nVHer+by5rUfUn9ZaYnjMo9unRpwJ8dA6mBICuPo3qe2QgDHqvmivi9Ov3EtQPSVZepxM71fn6+pmu94AruNW/wexJ+b7E49+S983e9PbQ/905UXeFKLVSSDUO2+O5Ybssfn1hEF4KmU+uVyOQBhqmxPBrP3O74XyaUAAxSBgxMpkU9B/GkeoT9PGa9pMudOFo/d3kATUIZL4EdGGCpsXXiCR8ZajflX3Sp8UrjD6zUko3Hwj23ZMa/90oeNBPALvFQ+xKzP6HjKRCaBz5r00HiaMQ3e7PotxIoODb00h6il/j/sPdeTaorTZvor3lvJwQS7rLkPXIYcYcECCGEEyCJX38qy7B6r/2dmRNnTMRENDtir261KJuV5smszLeaAiaKGiYHledKEJdG85iCK2jTXdEKK9vJ5bV3Kw8pG2VSS0fpPXh4n6tA6icmxJaGqkbL5+wzWaH08MSG1UgT4vlelCBA1F5YdtCkUnHZENxOZ3jXInW93ZTseeoX8xYrLfkMUPsci8A0ygRBtYznIRUFf3ZdUp+ENqVVRmRzvQm9RRSpm0lXjeAgkpuOc8WTvVM2mIqjvSGRWKMZQ+qW2qlBS7QItyHwciLOQn/FuMIiObRoMcp2qdyr6+ileVgFZnoCc84bFhZvbo43aRPZeLS3wQhZi4TIywCJTOMGJFfK8SS9cJy2WEmjWkgxn1osZ9V8hc0Fw1+YVS4JhcEwA7NcsDrW+KcclWd9p83xovdMw4BqATHL4nTDWrh80uWROXctbIfTeGfJY9FnmPO8UHw08oVRee29MBDVHXfMz5gau1KT5HIjz7T5zUMl0yFTg+m1QVoHuIvospFvKiZGmemSiGF5c7lIjzHC6tIhUm8fxHVKB4nUMsWWpyDJjrbpYbXlO1aC2HNqMyjFbmgtC8P2zCocL1rFoXqvXHgMm5FLf4/ikzHWYA7xdw4y83anplzqV7lUManNTz/moEYMyUpPGczhNYM5KN85aCm12wxZSI97lB7bPFKvyXcOBuqZdV1bIp6Dl81hDnhubA6ottld+hxwOGz8GEIy9kEv+46yKuntRLxpgJuUB6wgAzy/2H/HYVsneuTQKD3WKH12+ByfNTNoBmYsT5gNEtuUcxSrlJyz9ILo7ckRs01iMAIK8CFTLMeSn7R91yKoDF1QyKNbzil9qQ3YXtiuEFkf/bcPiYxeqlkfE9ZHz/rgCM9VPjLb4kr7MHLah/LtoyR9zNHE7/TueX54CeUXSGCcxrGpp2pQ6NVDCSyo50ziT4qIUUBZ7gmSGzLL587W9CrfSe9OqdDRqMyqdK4E4b1qiP9OvUGOvC3ITK/UlHHkMUPn7sTLpmBBxlBkjd02OwJIAsAz6UItTKYLiwqgy6gIGR68t9itK5liYFCsnd2kM5g1tieImgHGFn3R4rlCbAToIxb3dFiptqPf9csIUMYCMQvVkIOQjY+giRba83GFbK3mrC2O61yVHUe5bdKWnFMvhKYGDDQLPYGMI6DohIpChrN5MoXF6InEnJ3j1IZCMGSH49RKxHFq0scV98p+3zBr3sDiELcOUtCiJ4oh/uWqx5Jvbu0NrJ0RTzUdQ8fH4Br1ZLPtxKM66k4hoWaKE3OsDc8XToSFGroWmNBZ3E1stUBZX8w+tvkeWQSzjzmeJLM4MwJ+A6mlvA/v2wecCAvSXbI+BNpmT/qQAdykv/M+VNrH2GK2p/rtwyN9BGnJ+mB6g1JoFpnHWlsQrw9KWdzUQj4SDLxo2e9KwSC5yAHwUpMZrSnAEhmgTej5emZ9ON8+rrgPB9MOQ6VPDFVyiLcEDiPd67ige33VHdjr7/zuNvOiKFdAdNAXuVFshcfoExSaIzfg7WGYaIZtOZSSwZLf5Zb2UaCcINoRR7SVnON+F1irwmKIGlkbdieix6Y1piuOge04Om6mJcGM3+HRekouG8OOj+EdwhjQ/pCWljBblruaxU07zCNIiF7DhoLGA7hljn+6gKB5kHWQ/p6xSKLQAQQN68Bsf0KIWWbfxgcOWpNTDmKnHMR2oAMdHHb0TY4VnjQrVEJ5SakAVEXq40MOBbGFkDUwL3kfFfSxkEPex+bbB4C4Bu9D/vZhg4MklDe8j55pRliNj0gfDFENTYejwDH0UfE+bNTw+0o+zEPjqAyqEoZvbUgfyomeaIg5YX3o0IeLuQTvY8v7WEEfgtLwPp7fPmAeMrjJyTy+feikDzTh8+i+81iQPiq+I6bB+4igjyliGJfO5yHTedhMi0Mq002xtogNCnTG1hh7T431SRyQC9AHpTpr4TKkKLgMUQmKzn0sA+abRfoC9u2Ljnyj3HQ0gK2smc8W+QuOmmiAL0843qay9UEow2bO7HQ9ifV4trbxEhQc+Q5yjni9wKdcKcxp4qEvenYCTwfEKDMc2+NxcN40jJDVIh73jRoGssj7WEZ2i5kf63/CvSYTrQiVHCGOBO07hl3OOxsWS2XrjlDO44py0pYs8bZm/CTPtBDachlnRXseAzcvNwRnb9k6YC3zyH8ABiXLdN1QNwfOA1E6YFOb0dtdhj7Wq9eh3HqdFi+xYqKNXKwExnc8r9cJ4b32wt7TDpcj2KPBJxWz3eTxmdwucu1b5ZIGBVkC5J0k/GAFQRNEVnQa43OtBlHxcspnZHKKMbU2VDL8ElvpmMe/KjlEggAAQCtWq8yn5Kk29MHlFPEgUJiL+Hfj4Dh7Y8tMHF1J+ykacfTcBl5746izzSwHrJEuAD1XuCeCa6rwA5ZXL7TgyLbCvQDYpNEgNyl7bnyfk/ePfKcXTLYWCn7kAV+lXUscizUNzLKVjD/Xma6Gf8BU7RYWR6+9BZ/jCrzuexA8xA8jR0wnQxH4sPM05WMafXFV4k0Y2gwhH3Ldy0AJFltY9PA+OA6MX8F96FxW+T/WCcv1lMtPS/U5Qu6DzulYX9DU43uotrCHzPsARhyTO3N0gr4XBY/5lc4MUCYaeypbHMPccwxT0lZKPMd0wtpy/7RVFtUJnxvWliOVvC0H76ukMdmE1DxiN3NbDStxufLdj73M7t8g5W1h3SvjNDJHL84RRGtRaFC6k+HWL36KTXkN/Xv8FKu9xqPXj8RfOuU0Pz3zWGQrjyPPGXI6MTVW2dQD2jFalXEXZHGOIBMJ6GoNYu/d+LguIAHzJmS+2tb54s+4D81Z8fE63LtknxR/ws7/YZFK47wjJchXhcN84VilK9lNcvzMsmp9y1f9xr3NhowV2hJUP/4e2yglBVdpUs6ZpiMzr7JMfLzqn+c8/gBbLCg2q5Qh60BTdMRY7lYi5irMgyDwsHNAxl52xZ+fuWdhBW7zUqqZn6ZwvhHlWMuxkoBcqJwqPWANPqntCAldmdzB4i+M4+nX+2CcufdhZV+PVcpOCXJTtozYIMWnwed+MgM9mP2LnqVjbQyNf3/IKTjVXlgxYJosFKIRvm3WpRJ5TCdAIZPXyPA8LPNufO0B/0K8Oin8YuFx8RP0tZyguqBhbZTvCZIK5t1GulSU80jj7b3+tPayrFMV8hPkAoBGf3yUijWy+AmSW8Riq5DfIc2rdgo7KJK84qesVfCpW1ca3U8ki5ytFGoTRqalM1UGidyTjeVwLGPuzE9QgNeAjXEPwQYyiwWDmztMC0ZzTFi2nnH9oUXGnzFqZrX8ernxYeM/Yjn8SkLuf8tDnocA/xgpI52vv81Ptgeuev27/vWf9arx2ntcj2q1mJ/wVluY1YBPUGXYLgIvURHOowXrG+uWvG/8Y2SPuD6IRQbbA2wsR/Kf59af51GkjhfMMkQFlyQyqkq3ffCuj9wHq8ieFlR3PofVnzng41JOS3Z+sZRgXBNzLaz1r3u2sRbSOZdPNUPprSn31xcpH5N+SkeVUfEx+TxqwkJ+htVmpaRNpYrPaFJN+7kt35gHEgx03rcaC6sIXekRMeQTp4OhXkzBjU11GJm1A1d+Q4fxLDT76mjypGr3X6kjyd9+sWbj6RXTHDANfTgXbY39ztT+tAU3nFcsTgeu+wym+p/xumXKz8fLL9VCKPi+tj4nBVMZZOIEskDNmq67PbtLD/fY6R9FbO/R06Hmk3lt9urudIKIU9PQZxAhfehFL9++vzNQv6cLrZNOr+Ad3zuM6RUw+R2AgyQYnjp6C+5gNB2dIdr/swy1jAbHvP75BDk8bph9sGXxp5o06dP6OyJ+9aVG+rH+3C+hH3CM/KPaMrcc+EcJGXr2/TTXf7yAyAs/o+694q+oe3nDqYN9iphzTvbZ8FPAX1Dav6pyo/M/XkBK//d6nY9/qivDp5f/ug2gc0nBm5DdP5XFSRsVP0HsBay+/3PJK2X18wVMYc7vnvwZ1++e/Pj87smPz++e/BzX7578+PzuyY/P7578HNfvnvz4/O7Jj8/vnvwc1++e/Pj87smPz++e/BzX7578+PzuyY/P7578HNfvnvz4/M/siWTPhhPmzwt9yEoF6SEEa2fR3DZIb8p/LLWLzn+1ldGMzd/xtfbirzmzbM/f8SrLv/bmt4/fPn77+O3jt4/fPn77+O3jt4//C/uQd3vvsZhfe/Ag09bixXIeOSMltaz/iOp/RPk/w+F/hsJ7/3juO3gyHA7oo9v2sb882SMRnonaf0Sl7oz9td4/Hz1+h31j+t8k4g6Hr/XsG5P/xh605e55pA8lgT887svi+Pz76bahT4pvD1AKk/YL9387ZX8+82GQn4dCuWMdjtlMtufXnj6iD5pnf2YPisf1dft/nXB+vVz2+XOb8deF/2Ih/rvrIE2FfyzChDfxYw0G/J2fS8Cf/U8twOS/WIDxGfcg78o3NH4uiwv5w/j+usLzHM8L0jzz3/FPBfw7j1L8vrN/XnkLkIoWGqF////bLsrzfdPgryjXy/NxhdHH+8e7zPe8xezxdx9/9/zXluIdvez2sAQD/Of2WD738W2bw1/bx/aGnx2f9Zn9mY/0QVf+/yPtj//rPf8/sqfT/zFRN8ftDX4s622B/5VhOmW+PbvbbH8Ork35LK8w6ez6fF5r/MIZ/iBv86ogi6dcz9cHaUo8kM+PNhBbsOcVVnLb3PD5wL8eyg6WXCZdIv5U4E/wz7vtc/sfEdFfh/rtUvxnqJRLTFmt4Bik6h3y48VRg7oqSgWc7yYrtPbZyRRXJLWpspat1Rr/qEgnBHFsrS7dkhlchC6QBvHmkTR8fbZhKGCjXNU8DxZ0JARmdc4U8fHO1J/8UFOjqv3JMZWr9y8sYPNPLMD7Fxaw+RcW0P8TC9j8Cwvo/8YCor+wgP5vLCD6C5/p/8Zn5H/hM6d/4jPyv/CZ07/wGe2f+Iz6L3zG+Cc+o/4LnzH+hc94/8RnjH/hM96/8Jnon/jM7578+fzuyY8Hv3vy48Hvnvx88rsnPx787smPB7978vPJ7578ePC7Jz8e/O7Jzye/e/Ljwe+e/Hjwuyc/n/zuyY8Hv3vy48Hvnvx88rsnPx787smPB7978vPJ7578ePC7Jz8e/O7Jzye/e/Ljwf/WPVGLRjyIsTHaL1vHOI3zWv/skXYTSm+Vw/XsR6vew/voLq+sdWMKD0O56puTo+3QwzAt/Xx0Kr9oMkvfIXmahJ94OzYgXe7h8xEvQTVdxkV9llAopf7qkdOq9ZBu+K9427/jWGXr75jd29+xsL71d4zp7e942qf1V5yqfvsrJvfvV5Tw77je+O94WSf8i57V+O+Y22341/q78V9xu/L27xjlv1+p/o6p1Y5/0bxS/R2Xi1f1n+fGqP6K7f174WW0qv6KD/7dm9+9+V+xN3NIuHViLxmeP36J062Wz16HzQQKncYBrUortpD1wc8e/jgb0nLArQt1O6aBeYoGHutg3Iuv8SHykg+8vws2kIj78RYOWi8dJTe90qVUpC8FfC5bKLp5KRINiorOpzZ8RYMk+Upv7795NdYaz6khV1a7D3ekNhVZ7g9fmULb+aam7iGFKXlXY3lx8WdyWn6QWZPKTfDZ88yQSJX6xpJH+vBPX9Wf/B1d4VqvltQxg7wfa7614bz5x3c8nrfGDJtVKI/OPc97wnPgKUFV/Bi38yf3yFgrOxRYL5ZKEO14Jn/8oyOPkFiuLJrvRJnzXERXV52jpqT1D/FHwOTNhrYYmBXab/KCUb/DM6GFiqb0iNYUxJuAO+EpbcKiUHuXr41yZDmLNBkqBMtQ+jpe9w5k0o3DUQdVz+CvSOYziOdFGLqSdHClrto2Fu0BFTzzMSa9srBJXQi6MfycQY7T5rghOTgRSSPKZyEkSBk52vf7Bc/gIhdVUZN8k2RFDL77V5idk35nF/I7FUZJyqvx1fW1kK+uq1VKTXKhwmBRyhIJyQUKXWtRsF13/2T4+eAlXW2ufGVX38xDmCgttylLNkaD8y8D735Tvvj8UrThWZvSAinxilPWj0w+UBWvDz2tor+2iK+h1WEKrDZ8xz2F5atDkERWtfXw25bC21rjV3t8GmhbcivztuxORl6t8bbyL/VA1iLVPiHWlokM3lZA2rK+bSGeB0xuj4UTHgueUembAUh+aQs19v4kdzryPdQ0Je6vB0nXgr2BaKasP/U7WlQ1yT7KE8JvNhHtf8Zz2mjKWrbRwmL7djWefE3H1lWpvv3pAuOhaiiHWmVGOqVbHy35nG6YgMebomCZvl58DHCDR9lMGS2ha8LqgEAux9gtGRfB0oTfsbHxCbLihO+zA+kUyVjlO6pKqVZYFjOZ3w3CsgcfPyEsKMloR59nK3MK26pprlygb544DssIBR39ip0t2Yr4mHxMKy5KOX1/87AZkGTO2VopX6dvxrEtXidtUzBOZJR/MqIVeBrhgGcb8nlWyEYpZBvLbUbfhcLPlpwe8Rd6TkeudmV55iBtqAxfoPPWeH5BJT2GKH7RqmVwPlach9iPw35ynmkdZleWo104PW14zkID9Zwb7detY7nTgQqFM9jY4z91XrCaa0nhkeZ1bJUvE/KiAtkXDYVsjFe+tmtI87XskMZq2fDaZkjvsGirvVBmbek8T5KSJyGyk29bsv4nX16L//qnrdU3VyHJytp829J4RQA1j1tk+3/GpfK2DpACLIks3hbP94iFFmRGJfkm4VeSrI1rO7JtKxFflZLLH5PknV1w1jzmmYMdyLJdFjwFW+jsuRq1g85NyLdKv89qPKoh5CYVFJ1SiYYufLRH2ErOhfwF15zIYTpotNYo0pUFr4ekIa1qHc7pVJ2fPuWI/7l1vsbqIPEK65aKGZanLjm19d8dh+1yvITteMVlu4IiFCdoxLmZ983kGUN27Y8m0THZCs+oWKRQcQNSVtNl2vJMnVvcsnZHo0nvQsnx1fizrz3KuAwhYrqagLuxBbcXp/uzMpJfJ/r3UuWtI2jdDNmMFZ9XBHShEupG2VssDybPfFYgqEBhOqQyKXmPyS4VERa9T9l7NtdT5RrSp40tmhken5ma5+9U4IeM5omGc1jzCmYIMvmpL1KlEUEmuW8fJWR7I5lqCS/qPKY/0yS0Rh5HtCaRx/Ne5ggy0MlrKNZHzxWvMkkK1uEmmO6gdxbP2ypDW5CljraV85pLOdYGbBC5Am3rwrUiVvNO4W2pHc913XbmfQIF2ewEBfVuG2JaZXTdqkN+ckm9ya3NOOwczXlNNngeQ1UrOmKucYyhtqIFFWMIXaMZqz0XQlFC5LEjZckvqoko6AH1KePjnNGvwGuM7YDoFvKF0q+MfJ6x2qiAM49pbWlM1yAMyKYBGZgkDyQ8v/IbsLpCarWaJRM33pVTIKl/Iyk9y7SPPJ6zuYY8fI7ENBRscTGd0AGqWMkLxm2/VlQVCOXyBvWAIV4/kpcWqRtbc91a4DoiyWqrF8srHaOFmPXjY5UU940lAl1PNWJ1yhAWy/icakJEd1z1ufWXRJAiUU34+uR8fWQPWEhYMmmy5dVe9ZLkKVwcGSVYDc/ZTGr0hEpN18FAHat/USwhn6VyZLxF/ebg/pDagvKOn7SG5+20PTLcAdPU2nn8I883SJch76P/9gF5LudEUSW/75jtkspQKVItVIv9zmtizBHJnyihO/tdvrPKAjRDryFrBfs9ZbaJpLSQGrMI2bpLRsfbIhlsU23L26p4W6Q6giHLvC0uTSSQ7pieeSLbXJnSPQwQyaUpqKwgZ/DVnEIN2lKU5bMzu/SxA0NvlRJ+WDC+MqeVL6Aa0meB/7dOT9PbgO7sm+2soMAiymGp8O/QbNOFJ2GqMlDLdHBBYfqpGSmQ43AHWapDerjIjmsyKaF7Nea8vgJiPJdIH2S2dUnz/Sssf3x4gmyLc3nPKP8aMx4WkVoN8pzaBQoKmY5XYb1UK/AOsBV3eN1LrX5CfefXRTtod7JQPuOM2H5iNGGNsBATq0Q7jVMZc9Z7yBtl1XttklgTiyRCfzKy2D5vSa9y4TFOH/NeLYHUMPQ1j64ENpjYStAahp7AxsC1CRUKaFdYXrOqG/K1ZDulk/ohWIh2VH6qLG97q9GMyxumpaaaziseb+DcXZWO0oXSsZqF+BhBzlIzUjX+e0Urd3RkfuqOSgYVG2S8qoVG2pAZj6ttZj1oKcmzK0GZRNon4tUrSJWBOT8TKK1Y/tkHyaqsfs+EYbH8r4pESmOkSsxqsPD8s+qcJCQ2IMMzfZFTb0LakknRU0JQlvetKELbMmhbaimztl6kMoeDIra+37YmoM8UasitWVOhGm2BaK3kmK2vJLPKub5A8t5jPYbKKJa9OfRy0oBOaxH/sXzc6wnGdFVYYnBu+fj4P1hmWwgJLXwtH/VM8urOS2qR/7F8rqCJwiGgVoNSzBlGMCKnQi34HGymezmaR/Lw2h21rhZQSqSAqn00xXQuMz1RYRaAdnRJllLHo2OylS2rr0SLAKi4aUoaDPGSrZCMCZ8NoqFgOXBktXZIxROZVhzBn8WR0q3AamD6tB4BtpDmdNBqZGIRGfZ3zI6gOq1+m8vjU3eJx3Q9VWYh2aTm0HWUPtPukk9DX3zv2UFhdXT0cuWh+FK1p7exU2dqsPtI0IFHrTIVpVC5zlnleh6keN1mE6qbp3VHxz5MzwesUI6cizSaWNFaOxi7RehUs+nzaE9aUSG6Cctba6AhVGVcVFvIx/pY7yfXg3o6Yr3slQyt2N5HTeTSVZM/zGZr56Rkcp0El+kdqr2G5HSrKGMnL9FSbPOozg2ZVRUJ/RHz4CuyWbUHVpdSVcazdYY7tRV7b6qTQ42OaPvwjErbDfFXsMGLLQgqr7F1Ree+BaJzFo1Rof0w0VVSt/ZItUhPZUaXDgaUsxDOIdqvkosKNO0cqdXkyewlOU/wWBbaI0H7ODKTGGoxFIgV6Fx2VE/K1RSGcLuHrmV9njeszWMFe8WqsHxrY5HKlouzZcLYB/LPsYM1R8d+hRzRzmqhlEowcdoj/F1n53NuV5S2zQYTnO440QCZz/MxhEoq8ZXSc4CuTJ8kxtvZt5fIPFe3VjiSl2S6C29mFR4UzHuV8jxu1b63pAys1oVFKlGjwmG1yaC2wjMsC7OTRn+1sxFonbcTnIUF1gPtsYikHI/FZoopqyfrU4vBwWs0FrMSqttgeUktEZnJDjPS4HyUhdyLsjD6SRGhxexCe5ACPeQicDso4DDfD+E4QZb4pSofp3OSH7iiNd4YlyhPjWZup6V0fieAdSD1Qms6PFobDMbdY2Z21nu3ea8Nh7DbK+VgcjTYo2xik6qpfjFEkPu6xz9PEgu+KK+nzfPVrI+0NmKyoKtRPDMYZDa39vLwhfW3yr7KzXaSz4Gv9fOJWAanfQY52p2jSdZHV739A+S4eSR1p8MaPXFfGVwd25C+7POstE6X11S8P5ekuseC1w/cuOLu3XZBGvqVnarecz03gflsgiwhGFZILn0NWN0bZ2VAYdQHtiWjEeYjWo7q9Rs/UgEX8U53d/L2x/cFybE8Uvas2iCW5QAfuu7KPY4UDZt2AmhXk8/+rnuwj/OQ0rp2nMv4r6dC6WdnaQ80VVmDSwPkYFt0r5V4KL5Gh6hJArswMHdX+kcnzGDP8RA6sueGTaszOskK7guKnzIoH6AQJq26mU1Yyx7U55gk24cotUSsmT2tzhm+3rCg57MGCwoVMa91MU+GEzL6S/i65NF01YurTCD8rtAZiO7VDXyxwRqVnkVm9zAKGVsJai/I0gH6FFyH9TmRBNKnzvr0aJ/1Evo02hjrQVFouvPgOCN93h/QsH9/rTui3Su0MrSiROEn6Q59isVQKNv+4TW7NfgUOcJI87UgWGp+AZYIFDagNc2V21u1lMLIpgahGNEYrD7CKlwbdzwjcZMtN9jmaUfWc7zFXSfva2XprEZrHtEqqeVhOTx0HtQLHgoxsp4ftLFuwOSF0MXc+zl3A6C3U0K4lEAr7Shhi+3cQ2fB96QuOOOlXXlZVprqrMrOsFUdcDdV39FTdtyWUOFR8aefcAirpJYzBVNIe3YP3Th2MaewK/c28eT6pOykvDVD0MG3I8Y5pEGPZP3tDYMTpP/W8/S6nasi3u0XVFCZXt5oCZcoLXXI0PjtczZ5rYUEdmF01Vqj1So3mb6GCB+lBInB5OqA20lTs8ASfNBO38edgeWNOxRmxfujzVevxM4sWRflArQANBzQxdv6R/BSrbT1VFQw43/h7Ro9p044KUIlUQYqnJ0rrVn7ZL5HwVUHu/f0bl0kAb6jKMptZj+PIClaG0oEDvHuPQgtKUWymRhQrHfrP3HXymnahsPRuuiNIeaLXUNG8+KjsaNLE+rGqLWq4DCcG56jY7nS7SMpI3hSPCIy6um9dS2VuxeS7d1wtCuD9ogpY6N46vJ9ukw/1KsmUCtEDR8NNkixvV440QQoYAaI0/EaBdKi0Br1IBAbgHFJN3GfETZA26KQ0gnmokvMLC7FZEj0UDiee/PUQXFRkpldsE+wGoL1Hh2xbXEB9cEH1MScz2uxe8+tl1VbUOdTMmI4Z5/04dEasuqaCqSrMPBOmIw2ykhu7WOORdKkt2lFaVj7mKxj6kUU9TwTROgy37XvaAp0YnCsrwctJAb0x8knThXRXcvprjkZVugUADevilaGDK/2jhw7QmMsrT3Vl21Pmckl7f1bz1rHaqGhf/0SHtMOMT2DgiJEDC0MvRdHhoSwQPYp8rmfoOQwcNz5KDw5vOLFltbLg4oDkB7si/RgZYNXCMBfQBed47Mrm6GzljLCtIe/wLrUmpDX//axvrbTUo4B53/qf2M1YiMwfAy1Mvc0Gh5A0AeO23lfV4F8gq+oUPyJGPpI5/MYAF+v84i3ZXMfjp0DyrHm6IeHmCWCBTXYz1felkk80+QvN2jr5jEMUP4Ds1vTCKtQH44B5kjibYkwrvJPW1+YfddWRbFNuW9D4pZpEUwvBYq33EcnyQVfFmzLoa9P78mrdgRQYy+KOWaoMUsLWzpYV7CupJwanACVLa8GhRhkWyY1X0iPfyrM38EpsOZeoyDi9WTCcwtV7JkX0EEBR1j3UBPEzfks7hxjGygGks0SMUy+4KsemhLooB9OJVuOqc1DPItYKhjedeW7AaOFOhxI42fBZNIcqRXoYzWpN0kGc+GoJv4K6LQq3yn85pVj2jn+CseDsNnOt2M+GOkdsG5AE4clG55RZhzHNvdYl7sfgNMIz0E984sxehbMl+Vfv4WhxJeAprdlhznUc3lUR0Fndu2CdnIuV7wW1RzvTdHsBjVUWk4Ehmi4XJNWYZPAptDWWHK/RT7eDcf/5ynYv9eYnehGbnklL2JTXrgPw+s4BtkSEN7Jue+BW03opE0Bo9X4nox4rRG5DJDsgH3A/Bsew1yxjYLtUyzfeFsnvu9rTcKWBRjA1F8hfTHFLsCjgsKPrK2c45Z5gjlQq3w9BGveVkDawlNjbY2Y7YN82pbFvdUObwsivbAtLXP/ihLwtsCca5B2RTX3V05Y9V5iadyg4h2hlG8FoauRA2AQ8zNNWqAcjZwsB2m8f41j1VP4hsO/EURsvKqAtSc3lBf0DAiKxzHtDbTjMq9kYUvU9rEUB/ZQY5jpFc04th0DSZgMC8E8is0bLQkGjmL+XvnFwEP8HUViZIrufM8NgoEjhidhTZSdz8oiNaBq7jPzOC4vU8xb5pg3n4OPeop5M0+eEbKaN/LWJsVNGYezkMerWMZwNn1UhMyn4LG+Ber94O5etOJ1sey0Aij/yGgn1F4cj47hCwuLcQCD41JKWrXAR66cU7scb887GaAYk9KUiimCFaokjg01tNgefYWRSvFgPibwzAL73E1mIzvpRrWUH47+wMRmkHeuVsfgqh9OglJ1rp/sZ1hWLI9OX/ThXRn1C/3RK6aeRN626O9qjiC6BHgVYDOal4sf9bK4HS5YI5AOmX9YlWMwLAN9RkYIJaCQgd4MdULzhvOcFannZ/ZL5ktLubyckppvajFkHkrSBqEw9U2QO33kUTmuNAWLk9pjiWMjpc05yq6s+JKIpD6v8RYXkToSS3Je0LPjVTWnpAqXf5cC6wrhM7BO0RdF54j8AnbfU1f+ctm5zLPyrlLmf2tJvVi54h6p3mYIqFU9GoKPF1xXsaeU0ix59BLe/bYj1NMa33FXjCN+EqFV+5tTOytseWId4sROQ8x4rXeCU2IXOaNIr2b61IhU3JK9S8l/Z7493yNI7YaPM/D4WG4Ee3c96nPEY+N1nJ+0mibR7CjKvmX8ocVkB5Dsu7JC1nrFWutILS61T5gcrXkEhERqSckSp9eEY7XehtTcagymuckFqwaveAWcCQNUR7LobcLwdgOwJAjHYXwo5TapKnvgYSgUpol5eFGJaJCLFSstaUl0Lul3LojMRa6Yn9njc1GfZGWs3mNzMfhchj2pl57O2VxUPhd1Q3BTT2W4s1+8mIRvSI08Qy0ZltWazK+zUqZkLh/E8HuX4aAuJmyYi12xucg1n8uTbLs6PcZ0lwcVi950GoJ7S1ZD5Qlug/k2JINAvWrA93IQ0eiuNsgjwLGnClt/veDa4/QE628pS+4PwLYLWRrJb8iarZQnPS1me2DznBL+bSk+/870+x2PzCuzF5T3uWOfYfJw9LBExee1oBJdLXrue5J1gODbLZMs+FU1ZF4m70Iwfp3J4UBesxNbs9auVHIqhcVqzmekHpzsUR+lgg1jSgOenJO525VAaKDVW8p1bKwywVdcj/pkDZlVgpS9M5mnvGFabKszKXxjNQyTlu5n7UJONWLSWYr90p59/YoWoCvhjk3873rpLQfVfd5nFQnEHA0Oq3YB8ufGIg/aHanvJ0vHe3p3b17TP247W61t6hdQIqYztEv2HtbS7+cj5boeYv4gb2MQTF9p6KEx1IbOBQtGmAtSVzHDK1csq7OhXsn+ZkxjLVyN6lOqTKuh+kXJEO0TRdnkYkvPWWtZrgVoGdrKQNU8jNHzhcvhPMsHrDJgv7US24Yo1suU0gtz6R9f53F0mdFja33wf771GJA2jLF1mECQUfMgp0+DMHk1h0UlgammDlzfk4X1A7+Vb4KbPNIHiZ41+FcJQD4vOJGrAZZ8rN+3wZYWTEtM1BBfBaBSGyF8JfGma3B/Sno4hTIZXRBjFpZDN09zD//Ut3t6PWnE8v68dnnf39ObXl2m59fmVMAGm/mmq0grTECr/nb5bk6Cbzmz2RYfx9S91qTuHFH5s/sRf+s92fcNWbGFXmMVvX5h8pB7D9gxYEdBUwPcipVmZGSzYb2Npkv8O/h4s/GtLOeHBDQ0Q2PxdV26yvJLvJY20mq2I5RgfqaANuBvhtsoEM8zhNfG06mPukDmI5FW0ySw77Y7O5N9CJTB4FO7J+UFY3CGt7KeH+bQz/zbT77K9mZ8kFLctjFLSE/qR3zmUtm20zIbWbmvL7vxxzOATmjMsIK0+PSYnRL71UJsyICcVBXFA1gKV8UGW2AfkuWiOWxSwotZtF+r1eude5rnWduSun2FvgI464x1G82sDqP7fR7MN5SaqY3bauVil72yjX2uDtILaIfwRqWdPIEQy2GLn5VBP8I7mVxagco1chYVL5Zn6qU+fMQOLUg9SvtJqEfPAmuHJbktaOQ9WiUem8LefRwsxD4rAvzSglQQlYshKdqnP8+eZtbrkVPeysv8Aet/sqjOnjrJOGu3e3tdD47vGeVDqqoNBHRZLaYhME19J5vnxxbWP2An04L135njTNq8MKtfFMTawlSjJ+vQujeuVeKfxwNBdvsW3u/XhAsbCCKmSrX3vDoDIhqZj5mzc81LPZNBB5G1h6TNq1M/VS+PuSplmANd3uvW362O8H2VhSOgZAB0GWIu09dwqIqrCM35DSSs0yfpIoB+ZV9S10kIHCLbjAdT97DqNyP1Yd8ORQQrtP8rM3prnGZb9I9o++Jh/f1O8ddNADX6+9bCtf8ril/b/H3jwPs7At/4+7aA+vfNBiyTf8fyX48lGB3pbQ3M/yb2Sxcw5cuvzbuSHB8KbEI6Sn32MHd28MyAHw0Oh/n24AJTH89Ww7gfg3snuz9Koc53pFkne/+DR6LWI2d63Q1PnaUPgdE7dbAb90vbPTu60NT9eg1ujgs2gYxh7OurzpfpjYiTNeVRmcbt/smOo1MnHcRE6J6H1fD0zLDlEZeTyQzYgjwyl4m+y2say6QeeXRga21m1iXKZ1BhOO5NbWrWrv2eCLbN4tUbjju6yEiGrhlF41M3EarHuP0o7WydExh3NtrZ7u11f6kLn861/95MWd+VZiisxq2pvU8T8/Su11JxbaaXUhmddK+iOMufKNcpEsdaNxGFZjVXh7P5bhgvg0KqLjHFYJA/4zEhoIrI52C/7i3zsI/fbuqsRGc9OT+emwOtL/q61048oWvWcmsaKZ/Z2RT2srpenbw5vHdReu8p2MLesVoaTfy99eMG0Xo4m1VGGAlxkOQj06x6712P75Jf0FfynmE5WIXuH4/dZxUIr3lBXHGa54lOYs92d3+A94+NfNHzatSIfKMfBu3LOiXmS0/Mauyvna1rDWc+RZfUlN9FMJzFRdoH6rB/PVrX340Sw1uj64maqMWJH6JrL0+PlhBGDAOef+8SbW8P/73beeYpTeOgUQbrOJ8k0UZ3c5o/so2077unh//UzSYwD+kbSWZn7QaPag/KH14zks8SpX/C9ZvPJng8gMjLrhs+MPfLb9a0lLrbk67khuHrKopOl4EWZ+52Q094Zi04jj1wXs+hD3d3jtLQAOFWHD+5Hbxu3ulzK++rEb0DUFx4/LPT5QeIAxtgAaN/pK602q24G50UN/hQxBXdC2bG66UNGpB5Wiy7kbr+7I+f3qiyA60sO1fO3whpd4/lH341PoJWYMbL0UY2i3rBD0S/ZQiwvOlAs0D1lQFAQfHiHOX9etjP3cHUkqky7vcO/YaS8f2EMx9epns/qi5DF+nv3loK4X6VEmc1Ukc6XzFgO+/H7rQP8NuPskVqfjuYi/7PHL8VrpUL1krkHQouK6NBlujErgWKGGZHxVAmuiSAaHQEm/u9GUq+YH+me3tXd4oqx0vhMq9u7WC51h8q5Rkjjml9vDK5Q7h8AE12mWyX6UzUBz6l1Ib7Xzbs3FfdbHcIRa9dCPtEqqd+kSNqqfPSyds7aBli5/ji7TSeNqj5LKLsWkyblsZBc0sdn0rDyWGpL594nd0SMqudRk9h635jm89wSawMUDPoKwWvUELlSHtgp6DAisEY/K3F+vC5zrQ9Gk3ettTX0fNL1wbHpjxkndfLyXkuab1/WAEFgTSolV57JuRVq3cZflLY+AA8gHYnzw7vp/PRlz9nMvdY7C+2w/3dARvl+nbikfWKKouhM3K42Qn7W9EU6/XUb3TxXY1aAe/FU+H8m+2FqnWbp7Czk5szbYoMXXWYr7O+sZgglR3NFilEe5y8E1Pqw4leaOnTeQrL4fZQh3TkYGbwuwnPxeA12L+e3kHt50iWy6wWw0tkjzeEHxnyhJ8S0FuxVWV2DzVVo5cAluJ40evPD13EfrXu9uAnbME4lO1I0xd8rYrpfbzexiBK33ffD1EdsTjBBi2p/Wchf4wCq+rj9yPU2v16Z3+e/q4WB09GlwOuEzxsDWm1OhQ5WuTyGyAK8iO8xG8UMeQJ1QXbBh1udF/Qktm9+HcWia60COxSe2Qz5K61vrSTA87cho9pPV8N79IzZVQ6M9lZbc38PhYXz16HCHRlJp5BuRS4d2HSO/SImKURXNE0Prgro1AafpdHUht+pwW+t8SWFEioUocoy/lzCEQfV9iC+ywIx5anFxanKKE3Xi5HOsxeHV4UrO4G1cupnZzdozzApQMam/7pH9dZfMkdqA1eGMNxbO6UkW/RuZjTC7shLsXYksMsXcaEpWPqN2QU3MOJdXC3OmU2AaeFd2a/1JmlV4C4PlFOapFvstfRO2Z0pIjfg4uBvqqbV2VjQW41LCfS5pgIJOICfWyBjtIun009EqvuI2afZoVWipbdsCW2BDlKBtouGYKR2pnzOg28XSsOnWGDD622HqeutWUzz/s5E6Db88N/qTtkVlt4T4OAmVvWQOjPjO5kq7fMQyBvxhAOglndpf8cryjxu131mtdOM/Kox7j/YuFv136dsPWBHueTGsr1SLF3mnm+kTNrqA4HujduZRitEr8U8OMjVKsF/9Po0WhG4c66dUJ4UMurN6W6S2aXH7N1igJ0mxQvA1bULnIm8Vtltd5N7gsXa5Iq7v8wG77ixWx3BvsM6ZzvyMZj/MnweYKwany8Lq9F3S/1RF8wXmZ4NX/zjt+MNm5iB+8VYNmzUt9p87NAo9K5U1hFYzsavEbvyvaqybbF9u+zPc9XWD1iXK/1C76ekbN/Dz1fuBEaaXBHj93IfO2844PRMsNkMD+8ExfAwWr66PlAnrwV48Vou32UA5meYoOhLoUHVr82jSc2XnkVy4idSviU8+W2Rt/XIf6K9f5slvnKMsiqOiX3cBTzcIGnlZ2dp3ir7D3VBqwNu5mEvPLsFWilhgdQ+54hIarWZvRsqXKOrhWyPkqUPRJri/U20i2nDjWd37ER0KhXUVmM8uTKumVR+obyAHTsZmGOP6hUpGUXqzkfF91KpJxcGXIUN9OAmSqvyK4u+DTiFcU8V1wetiOHxG3IT67bG4PDarIwMDNWRcw7EQB1GqAxmZmw3fC5384YrFeTNQnJGFwVpGQ3+zlYDjdsHdQN91A3kTldzi51PJileN0vk+e2drQPPWM114L97QvrmOpO3x0ngwJvVHbC84kW3ZpJJnU1XuSU3uZE1tyq07Qsihi91vVu5Lp4IEVIueVzrdFqEi5aEbmoNnr+nG6xnJy8e0YRR0oRXMsr7AWV79hArHOE9YPAvTm7wXp4iwk3R0uGJCPlmeD+X69C7R/JAhUakYOimy+91Yat6ZLLWyBa7Z4htTB2Y2wy2LtzKEAj06E1pWulj+/v9jYxsK2KAvkplpvN7DSzqF6IyoR5yN+316PBFKyL9pJoTgGP6FeL1wjO06EKtLEsI3k/eexr5zqhlpXZBsyf0mbGemeW6YjSvVxEW8zEVaBvgWiPkgpyxyfF1rBMwm9fymRk9eDgUgazm5+fLytsGy3o37Ux3T13ZK+y/WS8nSiHWvDBl/EPqndzFlkiH5a77BlmV2t6WwxAVgOWp1Z38AExvgs3+e07YXRzxSUy5+F4Cj4VQ6cV4002U4dU6TjwrApB2WCuD9ql3u83Xr7FB2U1cfauFbJzZfbz+4TeiHr3F6wTH3bLXQkaAty7CZLlKq+Jy0uV+F00QCDfFDFtLp8XumZILvczoZ4TOy6kaxCymxq4a5faj8Om5m+/7n3oemztJY3Rr3YGWdZR+g3RsRZg3Gs4peVpQWiN+9bI7AHvPcSgDU2MZKPjDdhoN0o5uxPw6tbqlxTpu7w2CqZI1Rz/OWUqOjC9OXWy8bhPgmrS93KB2vxF0GB1h/e/Iftfywze8roQ76i7k83qhMdUWLUUP6wwBEzLU6eAHBeEvmsb2zxfr2IVgafPPTxt9/1MHi3xL2/oWVDRCXP9/ehR7YQGTy4YOE3dgx4vbElLiPsFO22V7S7xboR1RyV92UIFpvn2kiJCIZ+pFwCsmAlLwpM9LOmeA7+pOnGN17rFZsnwnOhlRPhPMWQIm2foZ3Evnefp1NJwuxB8ufOXUb+hORC+OFBCafSeWOYeyxvVE628ioTBUrSvZM63isaEIdnHnEgoxQXWiMGH5Y2W1XWR0lic7BJ1dP2fZN5J9ZHA06jsiTrWT0gkqkw4SqaVG7KjrbUWgJ9kzZmS4Lif7PqNQrmgJd9yhVa5SbRo8xznUYh18YM3mF9AtuBPxTTFAlVXws+aPnm6oPFVUp5ilZ/IV88cv7oiJV6VcR9Yz1FKbFCI/axBCpOF9vQR81WWN6JVjdrLfoXPt4XW2sIje1q61ZboFdr2euoV/FxTsA2jTIaYd9wKwh66prapAxH1Se9ahxXef4dcxBqn91NDFT0ICbQ15huNwNPQ6veZi6X/hygkmK64X9SqsZ1NT89KzUl8MPgv6QmXC9keaZWKNRAsRRV/V5EzohUSy9ZkJEVoY9mwTK4p8RFWa8SqD/Up8Jl0/npVZb/Fti9VHPCwtZ62fSUuEEVt7Fm1wnTyKCmV1fwOkYbKGG5qRTrRKPCBoZikgScFnVjYMi3A16PI/D5SYREPtJGELRsGNbQLSwthf0+WRWkKfbUL1bIsDa73eaxbHqX2rFoN02Yb12ytENMVjT4ic/OTYTXqt4TmXU7zcmZdcXt2qT3JvlVk356vvUlB/+fEoRiAccZin9gmPaF45yyJg0Sv2FmbjlheBPm5AhlZvefEg3prEpt5e89MZqVXdayslWzzbFO4yUj1IgO5zFvXWjGeAXgeNUppRyapU+QTHawhrpmH35w8MrY/fmNvKhHaqO3g0G1oawb3Q7sAXG6bUDwXwO8ESj3GB/yz+kGcDYjfBfn76tS7+xXo6c/1dLDMNIGQodObnBYSbLMFaQXcBI3cvhm2HrFhW/PKaE1dXd2pMl3vXIineQmvfus8SoHoHkrLs/WsVI2QxkU0kpGj3Moz1azb+ZWtigKCpWyCs3AePvB6tsouvyXNZlFQigGTwznROB9weSOQeGRCo0dE7W+dryuqsdZfpZhKMGeaVW/gEpmn0t3Zcj+6FRE/+kFaDTCf2RFNpbBZPJenupftfiId8UN7clpjezMa0nU7s7iyFr2FZQcBNE+gdoDAb7MJtvamAiFxUZiyiA38yg3bV4ltTJ+Y2yJlNFzu1s+CcMYAQIZg9jmIg4bQ5OVeAzTwlkWQiI1ii/Fh++ekqn3ArKv9HXL4PEeQomEFu9VhXYcJYzRVe8b/5IuD7WN3eonhhD08WOjqxPYx8Oj6z7Gaj+ke/DzhtE+lAcTyVJ/dWdyMqfRwxzvWLzhs1/V4IV5h33bibTPYD0NiPYBXn+VcCQowIrfV9NFjCxafK3CTTVaT3CKcd2oyNCMoYWtP8Z5waENVP81rHCSIbNfBr4ggPIhLimwEY5ApYHrvPPDKNf1yFUdLb/kc091JRI9yOrMzsIydxLlE1lx/CZd5Qonga/Gr4yPBfZkOgPyy9wl3uJKdCCqB7qA9G55fEwHi9A3LFkHjrim+SPxiVD8ysdY1K/Emg+xXUXvDLZlMPuAxv+7/5DSa8gZ7wDbBaMAEhc2L/Ckvuq1ANJ/r3KeSYn4a3ACzz1eYTxZIn5z3SU6mEagWlalKeHM+mRgnLrFKsZRbCvV+/3ls1o97IUtw/Uceg4+X7jq/KVmVEd5f6bxfNXZqbV7V+SOaUIAKSCQ53dDbuDzAg+FPQnHamPF6C2qKE+xstAsOj4E0feuPi7Apbu/JbmmtExZrwe7sK5/lLHvmG1AbV4WTXq1dYpTENzI7zFaj+32lTVqAuuaXZh+psXsRRx62NafqQlSWG/Aub0OI1FfrhEbqo4e9xArJqb/1K2wSvi2AoTtJzGY7/4zfHt1A3XrApYxzP3YOqDiT2yKTwx5A39XSvWbCjNweVD42i3R4vu9JOx4nE71QDbec7BzzFJJ2jcw8zW+SCwctsAoxkW8aig0rEJPlIq9pxOSIoW3tCu+4e5tPMT9oPXU3OsnjdbwdE4S5H2wPersbqYoH7qs8L/e1fP1pQ3xsen+20Ks70WsIIIFPtps1oDmcIieFk7JPJ80tbEp15upVbmO6P4FOvkz0G5FRSmowzLsZBKvJyoiDvpUzT1++XnJXx6PxW83smy291YFZgSvsuV4W2nCiZHYB1b1IPA3lcZ7yninL8TrfLm6NXK3HeWASb8PtdPaG+6j7yL3YP5B86a73lxsSuvRY5ByWbATZqKxFag5f1a2DE+CVl4tgTOIQ/CuGO1e1eAV+ZrwUV2xzkFsFHx6D4jfU5/45utgoHVZz8FJ01nok6PEl2MeTJ6wEyUFh3bqFegf3eHArr68PQaJQzu6ApccMPJYXAsJhvgr+mLiTZv5yiRWtDiJO7t24jJ18jfrZANTXCN3Q9B9tvAPpLrwaaRu+peByuCWXThiX8+xgZs9RYjgHw4sk9TkjEpCIUtmjKKSCAKzd3S7VM16hzeRom1iFB4krDwBdP72GddlbgSVdtQvoqJdbeZvTOSgLlukifbr2Kxm507S4FMbno52GOxj/Fut9EGkhZsklckealo4n0UmqH02lTKdQOw3r81RraY0YS55XODIpZY2AM3mn9CIuzXxpVZgrr8cr12KWahsxncK+g+Xp7Zu+j594u/G3RwAOzgcDsnWp5gbv3MM2BGYmbSIdQeZt76diyJA2FoYEnkBq77krrUGJiWn4NNzX6dwNWncgLDBJASSD5YMOrGZWUYs54WfDq27kbLR9RGbg27vDerh1rwr4UD4HJd3fED4bo6BZoaie9BP7Ur56Er/Fr7naQXyR7IGRaebHFye2gVcRrpxsJ3P5PSmv99o5spOk8djkgbuaJJaijsrHceYdPqNOkJez6fI1Ps/7de+YoxbNs/sIa/tJA/JfRUuGxNVWTyLCiP2IxUctDy/B1BWDJD8k12qaPOy3timXrz52gXKJX8ZDiy/l4l1X8a5/Oqs1xN0oCMRuePy00utwtZ6CXa0IUWd+N8A8oGAj/0ZVD1TMA1b5pliCv7CCWFRvEKDVbXzZU4/NfC75x3ct+fvaqahbuPVZnIFczJZDV3WJDwXbaFfFD/Tm8GgEmNDBdayTDGFxYqQTxMbwLizgDGyfewPCZBJqiZ9NAF1+xudiC8+2VzHGxDrGqsgnTjaOiumdzVzmft30cRgL96aR7uG9HpUTe7WxExSH3vhRTwCZvykovr32IuZI7Kz8+G4GZ2U+bdrI687DWBBD+abPb6l7CkTJqzoBG3qFV4+WmAfEH4IjtfJ31meY9Q0VRbr5BNJjINhC6Fi1tl3bmk9vXTOSlgfrtPErRZGPBijs53dYlGl8yytAwTx05SMyl7sSXHC4D8PYfvBP+5mi2Efl3EcqnM05Q4SxzQO9Q5KSNJkE45tXPcaCTCAIh6BG2oZ6VTz5+pKtLHoNZtVtXjv3kY/pYJSV07K04i6vFuT0X5nFnKoJIOb29NqcUx0knK1Wq9Bzmgs478zxYqzOUs/zFJDJGXCxAo6v4hWMiy0gtqtUwVmMZcpVNKJJkVi3uAY+AmFB8lkZW6KrGSfPXi66hbkhVIE8GneHhYKo1+hY6nWfABlsb6WVWtqlvt+l8mOvb/bhERqnBvjHoUdETqsjgZq1MkSDLIcqcnbCeQ7ywgvXrQrOz+F7HV/CybiTi88+EQbLtd0QLMzikaHGceaY2mYwPCmVOwjyZ0d0rYI400fd4VY0x9l2KhfrJFo2TP8MmT879VXndcZK1UqwDSd+AC0LR8iZSJy0eDRSkR9S50gdyuWA2vPema1bGz/wup3syWiL2uzs5qVjIT2biv7M330e1Wr4Rhas6mwvQJNy8QOJR11sCn7eTc+hp8avNsxWJAoMWfeJeyzNsaAn+pWce5Ra7EvFIMHnvo53pMejnb/CbWYo6rp9Se7xZYwGFPjTyf58kxt6yWUak7gvfDhMiWNs0GG5TaHY5m68kd8PfMBz3190G4looIZ9JVGKAF9dNlfIRVj2i2kZG3IGWzsBwQya33ofz6U3GjyE+3iWrRn2MKf2JkrPgbT9BLMtemR3tznbi25VbgzZWvdBY59qV+j1o2JixfHlbskGoV5nOSJk8EqBfv3W+j5txbS+NhUICctIPfng9p9Y3hh45CbEJol3CMHVDRoh4iyHxDJobSePwtwZisp69MJzP4ZFbLiYZd42tnpzp5sCt/vEVlE0JvhC4egtrdqsqALs32vm5OdUeeWoqL3Z/mW2mM9BBMZNVp7ltO5DM1mu85pE+MnPnFV0DuFUPAvpJiDlOtssunUdv+Pw8AhAe534op/v/fR1n0SPfhiammJtpLu9UwbWE63OaJ/ZzUu5B0t5K+jVyjTqean753a7mFbKNjXLR1EYkSwujP5FwF5PelWxAYzbGL42Uy2qFRI2iAXJfFJaZVaTORUyw78lfzhoxTDRDrMFqiZ3Jxvn6F1d5aVqT6UksfY6mGl2HjjkruHxTTF9tVhna3UnS9FTeKB4LbAA3cxRZOfa7zdC77zwl5GavO4firTjfgRinJFMB7s2P87z/uKD3WeKTT1osE119w+ZB+JrvHVLbXZvvQu2ukeJTWA99d2zc7ebeXPMDLSDHirJy1mXbgEjGKZnealMPnsVGN00XqPk2B7MnFhFU1vyCB82j8HjaaiAXZ/MoXpYp8AlpzEyvW5XiCcY0Pn+6g4DS8EKHD6Ii5RYwWLzPhze4mDZEShSeq236flodiJeg0UyMLtgWasobEcgA+a1dJJund0unWnUHNQjOROg6MrMusvX6tMbdXp3b8N8sB5MX+Ejw+uXvjP9HVz3j+Zlpp/ThyDlU52Pvgh8/B0r9PIPsFrjfa7GVXgpgkG/VONDd5g9j6uGXlm9ZxTjLOxge0Hb0amcXroCn7T41hRzrOjDd/LDxws2Pl1j5TJiYGPyGnwMzVS8pNfuEyQ/H87Ieb/f2sG4y8dPfhS36fxIyFyy4x3LIeJkW+pQkJdtJb1sMsJHHaGVetmMgk+pH/XOWWxzXQ7S2SzvqS58m8salUSTe4LQ5O7HJFeFZWlDiI1N/Z5gy7vtzLjIIUgkoXVXxJNRLG2mBWvn5tEcJge8H23SmZ27ejRIm8/ufX+yYF+XhQX3n+5djwnNLzvCpvK682hyE3sdaI0qB95qnaZaZHuKfDIHH72D03qCEVwmof3edVqU3AUD+rZ53742SsNqPr5Mmnq88JC2m2SP2indl/XSQ1V8tQPruJkfIevBXE0vzO51dnCz+9CpvW8Vc7xLbpZFeawab7neAB9fEOLVJojh7PkyeXo+nlzSr4tkKZ3S02X4yWcxsj/CfJ++rJN0J4x9uB5iY96ga7T6jtMQ8EncJ9e59hTvfulJlvZSBf9+AJPxXmhv+yzS2MmJw+y90+tavUGwaHDIRTwr5+37WwG9rXkHbHdS9t2pX4ZiOpzuhrWc9mSoSm8Q+pA/+uiUWEa/LDI4dt0oTqxnFKLJsPkcXx/FkN29hPkrvHxqaHRcYfidUSbNRIQVfXXZYFwh5aSOLsneLZ39IFVmB6M37xuPaATph3pkpbmSY0XvncOGDz9LyygCu+lu8nq6Xy/WtSw+O3rXWrW2hABML7jk8rWazL1DdENos3vlg+dL9+KkkSSfoh9qaUAstDgej6d3MjsVH19FKCUwPpeFLc7mQeZfwhbdN6PJpHTLa+fgM7Dvss+V7N1cCS80Q4x8fXYu+lzbAkXu9jK3J482aTqnpuOx2c1btP/MQzG+L8r2vdtDCnG3ibARvu9uSnseNDZtNVJp0O/ktizcdFg/tZ2Bd94JUW8In+O7s5VduV1QWSw5mk/Ot9rYt8pVkoMkFJjFobsXpP4nj6ZbL27cZUbjs4oF5dpoPlqmytC64v6Vc/cYj5Shbw3tDVudIuXc/WPUYaMuTrGlOsYiUJJaOhMmISOPraS2ai+F251l1PSaF0fNZ8tmbrG7Q4Aqlr52SsujVrVem4ZTd5WVlJYfYUvO6x7rbnIg72DPvOHCgzx6DpHa8nvDsiiJz9txfiXqAj4vtYRXfJXNDdkZ5iXFZbOhRk+G/s4jL96ogbyEFiMr7Gzjcww/eJs7EuuUnhkCsxe1rW32a3CpvCfeJhRHMXrA+hPsfLhheZDet9ZQlrOLDHFX0mCWd000w9xpKQ4EOlJ14l2pcbAWGluaA435MMpDuWrAAlylYWuLovWgXvWmZebJZZKgJ5CErSiHMjGneyqCl5WB3ImJZwhidOVNEixmYcyN8qR0EmwwXZrTPp2dxNnHxrrd0K8DSwZ+MpSouqnv6cmd95W5zR5wOmBg2sXCuzy7QFbbad+CNMvn8oJw7nV0JjKgUZrL01PfImHvjYrchmkH6dl6XNvjNTnMry/rRrL5qDAh8v0H1igG2lqDcQD0LYnLUaO0ujRMp6dHaR6XiOhM3YDEVDadTL+3jttzYc4mCpz3GIl49CDPG2n6titvG069pT4XPvuAYPbllt2Dy7bp7qZF2gGzHVhG1VEm6vayRyaq8U5NO8Dv51fzMH1PaT7S7/yEizjV1o/O6EA+ST5m1u9qZF1GC4R1LKnCa24LxN7OPHrvsACodKE+/dHc1kCY3NfZxb/qmIuIs2awi4K+krVIklvib8uON2oXpV0VbfAWEa+1puRKd1oPPlpninElTzNrd7MosgZMlelGWVasjB6mlC0mN3SVk2A9nfWhuicpwIPjAxCY92AwmC2p8H5v0+w+L4NZVNxqyPyqP2sHt2uL0nwOEKW1S8pNLZ4nWNjoQzV/L4iHAz25Nvj4lPIxO0/wWqEi2LfGejSYrhaWfPEH+tFce/T9GZvXdIHPVIn1oysKP7PjNbpEc0jR+k5n0+mxAtRL1Ag+jGbdkaz6VB2K2kn+wMl8uy8idaRSKvNz7Rru7laMDRqNvKJ8CLwZ9+OwnopINw/BEKaRLtxClnZYKopaohWd6w11Guvy/socMBpP1+JjbEqA56fibdXK4fAdbJr24w28P9Jtqq7oGTE/7iNMvIGP+afQbLCM8mGN8PzhLtj2asA/i41K7Ippz/KoBcPavFsvfHpX2dVftrYqqHL3GI1OVpHZuPcbyY8kT08sNn89mS8Kux6b72ftwu5E78kIrwBwnWM52aYH50X8V/Y0JVImQJNJouyiaWTi9o4J0Qbg3jmW2aMU8/lQ8yYxzTaq8nEpwflotWFLeIODNNQPsK7hFN5nOtZ2t+uC0DXYBOyynDkTrFb7TLvb4Yg5WuU9UjDy360WvvcrajuyXIaz63FcuyVqzrV9eTSr2R6LxeI60d+H6xgfn6jY3FKqq+svurbBZwWTHj3x2vZ4bb1jM7FfGeYGcXGsRlvpYP8/7L3JurJI0C56SzaoOEz6vkeFmaIiAnaINFd/MjKTVVX//s/sPHt01qCeWt8CsouMeKNvO8gwAZch5fkCCuSQYBwZ1MRdIicXkFeVr6Kw6O5cFatpGxxpPZIN0zPhBu3jD7pb49rptijKgGldkgHLjX0CCqdyTHYfIjn9SUqBT+J2W3p1KmEMtnuqSH0O7D4XguasXy+CqZHIZK7+xlf2DYhQFOtTfqV8MX2ovvicS3rxqHd5ACLgAofRMQ5XaOhoeF6/cfgNkuLTxE3vtpvcue+ilii+oOIV5ZiqjqhOizaodSSLc0xVaZAFXnjH6u9IUQN9VkyoDqP0rzxolglB4zmSK9nf+SOvliNEHrqnwinELK117XN+3d2U+M5yps9htNWUmafPKbfXvFtR9/XK7jYSiSijGMveJu5s0ItrJYrq7mYiW8ru2nJUBfVa2eKokYzjeCRUiL9trLNOWPELv4dInaVTZEU7mOlCG5PrhTAdIaLcRBq/rxANH6z4dojnR9nzfiQTMLFjh2Qu1vu3tg6fhfXa51mJ12/km93CH+WRaoajQaPfO637Gkdh+DS+mutLR8weXliJfcEvtQOJ+B98Ij1sgW+jt1vfsc6nZim+zcvqZuXLkSClOZXoYvLM5oYZnM3Y0irgCHYuoSvXbxvujak9zJP7ZgFUWIsVkcy5Vq/LRDSXIPdVDT/92C5Uc2O+5rP4T6bwC5vgFVveuuFb2X8W2lPspOtXKvvFeWseP0dypxONaYzqsQYZXki3KC8/Z7AsImv1w/foSvCDIIaf6jnwJOJpmrs28nb8bjXAIEqbgvwLx/swe5ix0Y5jdr6SndNpjEbnjVycCkcLsxOsV9rXk+sW9tbkjJkGJ6DeKBcf9dunOT6SF7/IUScls2++hNrHGNzsppFVdmqXqyrp0m4tFecjViXABnkzfP1MI8dGnWLkzho5O0XHZbLa8oMuIbdvN8Z3R26gsyJ1LgabVulPxDQrZotKI4CkBb3B2ZRBrr9TGgvX6RylVp4cgK++yhazwNMYLcboWXhFJaS7wQO6tkWXcSjvFsyEdzYSbeNo2xhA+I/BK3b+kUcgz6GPAKEaZH9tL77t2/6dfT8ulClDq3pjNIfrvH2VJAFbQpEw7ZV9RebDWfFzvFeIKy3lB1pF6Jf4dXybVpQXbyMa7V2jsxSqn0EjqWCbzscYCSIUSifA0hxqxkpbiapI7+9YCqtYN8yH9XHw1rj6GutXIBOQrFyVgCT+oo7KESfyJMgQjVdqNZ7CDGs+vSUe7/rt0vZwXdc1jSqL8QB4BkVbcsi1zP60TYLsyMvKpSRb9TZoQrtvY6aze8m9GYc717hl89kD60T8tzf88hiTmNEzW9Xb+EW6+TA+WBpmwvuEtRhwVcdyZX2/N/eVkfoBhyc9t/OlKZ+C4eZdiEDRbDL5yA8xKq8F7IAS2YTbm0LrzW7ISLG0sYXX/bAcZch9VUsFSYSr6SLWXYCLCPH7NquB0QcBGDp/zjbtIMVspaTG8UwjnuOB1WqDecxdY48wztBma+UlGhinC3OCf5zIYKNfHn4Qd1hPQh5EGjzQy7AOXQPW4XjiBegNlrmdDPxDV7A2JuXXqNyVz9dM/u/X3L+vKch9zYww579/KGVuZ4T2n/L6LChRDhqKqWOd9qq2AjmLknIJ9zPLO79cjYGOxMKLB/XVnehZbCNa/+std5tI2yTPLl3KuYD2aWeGYbwmmqL7utMKoOI1ys3w0t83aRFLSIhe8Lu7aLGSiSUkeKEZN5iblyh3g9bwRWQ/55gvrvXLsNPH4rGGcau/cfNNpIPUDMUeCwYhL1F/ane5609jF2zsA4wFMYpYdhrBN8NiwZq9/QXKiNRb1zTvKDbOLroZG+33q6M8QaGtaeI62ksvDzN/YEP//qIe2muoWRKpv6pSet0OL1omEjsqk27VvtCOgRwoeKy5+pWX0G/RfHCL2bx+EBwnzZnEfQZDJhTnhXO92gXmgSVmls4h9nlzpoTUrvXIANEIvjBjnPDhh6Vxg5nlLyy/ecQpwCkuI7sjcvZA8WNr/07OnqzmoUkaRg05WHhnKbnFSphrx1QJlO7p73RVQaI/d/Kl1Sv9pwuMM436ijuqReQSJjPDi3mMZkwBr3OMMyfVDqn0dVc0SqWk33XCW8mJpSRmSLfq/tRjWF+Wxk8G7VrqDjlVWNdfwfAVg9CNchgzX0kPNDsneJCdNoQMAwSrsG5+/ozafrNalXjD+BtFuYdXnD9jDnad1DGjX+UfrijuHmdgrxAfcpJAJYUaEVAXbs04dDWXX5LPb/OlifX4p/hu++FYnV8FHwN/Fv9mcDU62VBjrJWoBzhrrTfwWZtw1lI+n8b8tHGuR62BZ2L+HkmeDC2FVHdaL0VE/W5TBHMRY+jNqNPakcK9LA51nKsXqnUNTJtcOanhy3RfHlFyhfkPjWDX2RfvBxSah8BvypOk7OAqqvPG3+1vGubn503h/+3Kj80va5tSN8qN6Iv5qcaYX7SKvV+HfCqngI4TxivD/aI93b9RXtO17A9IdJyS8nwRPAewJ+IBQgo++urxaOoZxjoq2sOotc/T+kUq7dFUKpgGs4qH2MRVjdFD5kZyHYXSy1lawM+wQkkugGdgFBilrahHGFhW+L7yCZyChkymHa7a0xtztw0+gYcF8TxcYevNnfKC0KaV0XVTWdRej4IReJGEfirxwT7Q1nlV3AjR3vLdZOfx8LZicA/cG1LFC8y+jCkKzu7UgzrYpV+WfVZ0MyL7d3dRMV63dRXRoklbypONztazR4k5AsZs1wPEKDlnEezrik03376/gItyhf9ERrF0u0qndgqMyhmuSZ3Xbf++gZdPwTeLV2S5pitHBpU5Uslx2cr+1BFESI14IEOzX4SmpYmm8Tm9LMzqdaQbwIWtr609HkBRoU1zC3Rj62LBwgGPi/0Eg7vuNmRUz9WFCXF2e/klyh3WJ2VBDmqOvz9eBwyiiI1FSxCtlBsyT8XTD4wPHLfUnbCsfJA5iWxOVs09n7Pm0IAlKgt1yAk5PfJlt4TQbVsIVJmtLs9WDck9lvjqyZehGzRZ/4RrDSccUOmwskw/F31bw9IBIt4rBb2sTg6eUMGxoDsGflpBUl1LAb1RvMhXqQU/SycyOnajoh/z4SJ8sDSaSVA1zMRnkhQkDhZp0104RcSblWL+3clPESP6D9XFbw8jvqOA1IAVjb8VJBlHVyBwu0baKCS+9lywO6PRyKXkMpwe8xiyTO5wyR/q3F35GRCBQG80EJuCbpqQw20CHTQJYyG85glgTB2xU5K6tRjfcqhTwEvz+3LdER4rVzeiVZY6zfX3Bc6anuOMUtiQKAExKwta1Vm7ZWFsvQHpvcECxts9Et60Sm9o09qqug73qkNBl3jw+375jjE73TNendMVSlLr+yXUb0fOXV1IV7DP7fPGOIckl1GkjSlkhLEJe86TsPhaYtz3JiV6Y2lF1yYMGQa+6ueTy0g79uUTI1CyLDEeac20/DY0hulgEE50KOpEQ4Fw3hB6wYKcfQnTcoU4v0sw0j/fl2Ov9mYTKvbmxUgLTVQRdLqvKnvMX5E7Cn0fxuOTWqBFRn2KyYG1wzRUCZTNJhli4XagMag+Mpl1dHcKZel2uJ8Pe8xxNuqzQslhA3HjmDuvKNqWzO9KxHevEjCEuOhAKbqwTEgNasH/PWNZUbkKX6PuvlF+hwK4dWXO81X0IlxMSv/znfvDwFiws0imk8irDCPwxax8apIqdpjDdNX7XMS03jOzlQ+fC166uQVnUYW3+qK7O0GI2gMiFaoGynnEeDPTRbFNZKODymDhnHqyBdhW8h0Rax7lytFMY3kDwjSwhPnJkfzu9XjUmvTfzw7tqZTjF5GuF/OYn3PKdYea1eazRmWPz4NDAj45PPOOWNlNZsfxvxhYhPfTYoyfGNlivEjJHaKRR5ZV7J3msBOZKHzE8JZiBHGmPR9ias8S7ydY9veVgAiXX7OOgJfJ5h9bR0UQggZuTCYLbs0fN8SOqDU9ywlefKGekSc0XdzR6YvFgVK3lH4KrMWr+J91JO2pkoM2zOb7VPsHrCrAq8qRA5q9SFGWyPQFhBb2L3H4xMS6IYSYZDQzaaEzPcnUjsnlBaiZ3EVhc9yTIZyCFZnFej+G9VAKCvyZ+zw2tyWpboSyG8UXyPt2Z6yVZ6j3aCaGyDwO6LnDSFTYQY4NyvKd+6DZhAwvImkDoXxIGIR4nb1296/9IDG7QsXeR9uPCvPhUHFmVUmbke36ofLD+NgOpy9+pxPkKasgqUPKi9qMq56K+IM5gZnOkEP5Uycj1FUTRJXlILZLI5Q7obPwHptG4c3JHX1VFJuj1tvo0AfoQy3+O/JXgXlfBP9TAt8ITEyneaFQLxTS0ttf1RwjKmNfN/H+kLtSM7rNlcXXgHwN/eQaZJuLaNrv9wbrRDYKXFp7Hp8Vzf3ydXpW4MnBAKT871hSMxQVxJfZM3rPkD2dj9SAm5dPXpEvIfv6eOYV5qLwtgKtCVitHhOV3od2DD4zHQZzriL09b+/M/RUrTFNxvLqibmNewpplnmDmO8Hjl24YQnS7/6P3QCfiH59hSCtdWlQWW5tOyeSGt2NuUU4t7D651tSxKhKOk1Ugb9jNitaV2IxWYmnH0Pc07sj4rvjs3/E2HVBOUJspiO+8nGY/891TT9Y62QyG/7/uMPyUiweW4hdECL9nw7R4iIb6Kle2KlOP7nFkOj0o4szZhf5m88X/X/4ji6cf38ttMWY4UF89/er6QO+XP7fndP/+o6QuxxF9RWzGsCPKc8eKNmtSQ1HEytk/y/nkR4sUzQP77/zlv906DroJhqT8k9f/c/u4Olfl25zl7Lq8M3EI/HPc0yoJaKzkr89JbYXSbhbPeWp/T/Po+9dAlscyQxdMSsC/BRf2Z3NdAIGEoH5ONXdERPRbQTvsr5qWDWKTpxRmz1+7vDv4/vbUPlTCCrhiLnB7IadvDkmkYFlleiu46co/mJS88k3yr95q2EG89amebOYptwHn71Coqnrh8SRyNWNzXpwOvcd7BGnF5mXkzjk5o+OnB3Zz7S57wjg05nsY3MM5Ju5DfP/cLgB5pXQs7q3N5tWOkEHZlGvsnZ2BM1rlm8inyReCvluWqO5ynb4PDX7i8f9nzwbr4fbNpX9mleYnzVpjylOvvyHi8okutK+qc2uhyiDrEj/N9mTE9lDIwixChf8yTPiItJ0GX/tsceYkGwasl8lo7a28v34BFSNdeQpB/08YQ53lI7xDHWSkoWUczUDq5q20K9RDTk7i7d2PMqicGjJip/KMOG9dYXh0RW8LVLR3ahlB3nMxoHF3mJMGrMyu7PxBpkoamcxZhyY6ZAWPowSk60A+ffeUPpxRXAolldWSbFc8ykv9Ur/kkZe74LmXv9L5n84fBt04SEu0sIill3xH6z5OqL8QYKG8NiysKRR68mNdXZZnDHHtEQFxKaOtZ65OaEWVoncKQ7xqsFzc1KMbQ3wHb1hWCxtmWV0+IRhnsqrHz4PN2XcoxHp+jBO+1L8fsUIQBIeJGYDPetp/ADLfU0gN+pR1M+l8iTysGHyUFHHpu6znWmMAcGX7ax7xFe5Dv43fJkdnfJFLBUXY52fy80bKj6ZLH7G9x4EVzZY7VDuOo0qgVndaPSV4LsyQ8/vGczKzbA89eSvbw8U0Sg2zSh7aj3Gl0XG479zSknk4kW6/KMFzKSbm2Ocpy2Y5xG8PibTtICfzv7znXgFhTz5YO6+doj4RnVK+Ubws7BmAtbSIcF31rlpRqcqlxWN27wye+Mt+G3S90yk2oq0WYthnokT7+aZJnd+YEycGZ2MqS+7xnO0NmgNjHLS67s55lH1lWg4LwESqHMuxkokQzc5YjYLzehFvy3bxUJeV/h7C132zCtqQXPsZHnStXNF0AdbxbJfOwUYdIrHCdFPHXdmT1FuSQkr3p2LEfEu7UlsqRF5zFqjGTMz9MNMQJbonF/5Xj6oN0HfbaYGC0x3lkFD3+my7MEf1BEjFz3kZfkINCIzqXSJMAdyHwybiQvOqAVJSZjtinaBLO3zEd2sJ9CMJ3m5BpEpuhBRRCPpoPuJBx8q8F5n3SbSC72tQlH4kvuPP8OsAnyI2eABfKgSJ65mcipJVHeBSf9ZLAusT4/H5PTaI1/i7ljHkR8FQWP6xCMksDPVRQlxn+aAtuCbQ4JLIzxQXpywntiYL8ONSJGnqzzbREoWZEdZnvRXQ6UosdSbK8ff8+a2qG94XZ1ynskvSd6TSGXEU74hDHbil8WMw8AUjei73tKrvKPzUcY9vAPr7ZQrryOFWUIwJeX0ZIuehEiaJtisuM/hmFzFm6C8JxsozIR6WYm9SA36tMXL8wYh7Vz+TC2AMbPqGCeZqy67o2hIcCiSJxThjJ3+gZ6+wM9vpn9T3Cs+Q71QC+bhjf4P29WIaa783JO7wEFFkdwPjzSDVgaTIqG4q8e/gxAoDouxUvSH7XO+tEg8yGS1tPpnP6QW1KjQqs9tUQZyejZLO4xYSimzm/rjtpkfhcBcLfXuCrXkstuzrK93onjqojJZEi+GiAKuy1BtXSpBDCbbEfUwY130+uru5RpzeknwPs1BbXuSCfuY1lXCjj8GO5ovoErvbK0ar/0fx1mp1EtcGvbNAKsulsaeOKt0Q37QOvj6wLqcPTcC1g4sIwV5CNZP43gSrOEBct8QYzafi9SbfuG2PdoLxGd1c5zJejbZst2d5n70/oZl6GVXmblGHBkoamRmswXTKl7rCHUnR9nGVHu97eshVK8/1YX1yUI4WYHd40I+idwCgiSf2gpv/b2GmgJ4ExnViqH6+9ZaoP7Cl2THYJXVdx/8nHqjOd1/9tTvy0SpgW+9LJ3/4m5SnWI5+W4zOlh622Mw7spNJK+hMevaCuP8mVLseZlWaR2B0eyJxf78upHSl4fk5Xnrbb480kjBgUX1YQ1yOFX+s9zaTm0gGc3dbK03qxKL7x/RV22Gft1oif9uEA+6+DmHsiDeCUKUypTdsSVwz7sZYqoyv7Y7K/SbZVwFl1g8zek74bLEj1nf121d+yg3P9xMV+P+s+kjeqIZO1Gw6z/k+xtozBV+7mYYhY7NnmOzxxr8mfqmRG3L/K2eAfSfyntM/45gUvnVMS/iu8Ojl2CY9IVdVszm++ibQwzAMxeZFRLrbKURQZW6co656S4OMvOpLEn9IWdtMcvheYfv91yON5GTDXi2b6/u8vf6gil8RcTuwOTcExnUIlqVRlm9APOvvpkz027yFXPpV0XjqP78bz3wg9/7hiGeIiEJop639aZaO/rmMW47SEPESBjlz/njcbtx1ttSUCRHrR8ks+YtfqxA+ObJaxvcRiw14r2k9hAZzp0xTxJFmg8jDPvvAvyQGAxaj2r1eBZG1qePbVQed4HKX8FALK2EZ3l9QtSzkK86Kh/6Giz9Th1ArYSl/0aHtQo7nDgZXYCd3k0fk/Hntd5j4K7qBc1CjH6KKean1+vdGheQy7km008KhQFZeOW2e3Wx9O3j+AVl4B/bmb3YUWsEH1EYpxqYF6EhWW1kIREls33NKhc/443Em+cLTAfgNmYMOevfw6uQO+npLYlPzkTz3UF9kSg0LaccUR8tfDPV8Y2ZgvZuoR6dIfGfLhHOrEBhyzTK+6yKlFOqn4yg6LrtLqo7AV35WZW4IrU/6wOlDlvCGGWXcy7caOL4O64w0rwLnVbuId8hmfi2nT7IbuEN8rG2I83m71KWnF6C+lz2f7+pO8rh1ubVPRMTTOWBtT9dN97bTnMWk8D9Rd6sDjIezIPKoJdlTlpwtHYzpJwtGUBQGiRMCfmNyaqeb5ptX6MWS/xZvmj9u30L6b2Re3ZGj5cf2p1ef6BnjLpeCXaeLCGnaKCpHHiIaZ/mSqScU32D90loMXU3Stv6GJWE9PaqVKMU8h/meDYyWz+30OZTqsPuRnUeSXCZpndfpWf+bu8yDDoS6ZzlaqqSTHC2NxGLd9Kd3e4m2lXSIiyRrd9dLYJ7QwLB8DgM2/TtQhL0UYZzX4ibgy6U6XXISTDJ5HXkNsc4N2QO6t4auY1Oxu5TQpkuMo6oMy3SrqGu/MIO01H0hdzlIV/8PuzySuXex1fxaEjEW9+wWux2eHx/bk72tmWIaZLxWQvNOO/EoPbJ3Dorp/xRH67vlWgPMI5TNPPooMtDnJR/Z0z1RBu8x/povDdGGy2gjtT8GOL5LaXdLsNwnxCuQCXC3Rnf1XK1DO+o4PHzW3jeiz/r5XG/knMjaPzrDWp0yQNikUCQZuvGV0j8199DWtrIQsGq1zub2cj7hnK3RHjaelCf7Hl2Q3txC3nW0cF8CqI3cErVwO09sttb6yLJaNNeQR54pyK5xehjL99S1/Pfuw7FEwpKmDrTWF+N/UP46yWe9YGHTgrfvmo5T8okj1oQBpdRAMxYjHPweQaD4os8dD0QZo+L2Wi6JwU7bp/lJFrxbzb3ndXJ2zu/TDRjN78MTwlZg+4NfX7XO+KjNxpq30iUD4/5Xl+/zvN24DFb7s/702UETpGnW4JGVcw3CHZSH/0wk9S1sg9tLG2xPrbHt1F8NLFK4seYTideZd4pvMX3PZ4QPOdpmJ8tIFuDs0VVaO7ULVv2LObw8uuD8iNATJB0/3hrrNnsICrI7shpmywuK9K3V/Fu7AOoKNI8MUUBvw8t/YEGr9zvSfFX4y+azd7e7aX1SM/LdsB/Ws5PpPqgjmnODNlMI+rNqEXIndLUsNm+zAyePmyu67xTr1UyMuqVXnZCv2yAU5RPm94dPu8zaB2Hcefx5VMN2nf7Tt4VcXU4Do3LkQQhwqRrbKoovymni6ifkLd6b5NMPK2CvFdAiIH2XIvMn+Y+hnBmqZtpn8VtJuyvDdXuBbscaLzaWZyViRtdqsdhQ6NP3utHmGHNTA/a+ff8gxrIqigyO2N9/GGyuDWVatUKBtz28vL00JZ7d1mxumBpjmd6Y9wp+r7w3KNvdsigsBFe7ypxupZYk4yc2SOEF89/xVeDooUv2FVx6euk+IDES//zHDJIUtTA6xi029BhSqz7lwcEbXIayexnJ8yJzxLS3LEeguf7Oo7QBTkoNNufedd3QvqM0J4MUlG/Xxj6/So9Gc8LfItSrOzYgjZeBPN0PN2aP2msluPNebj6mvh/URgHwo4YjYTeTw6vvdm1KVaBjuYCrBSQIhkn4ue6GaiQpFHKqHTTCyoO9+qxzPB16SApyztrVjmSNjVhT0sgU3OUgJl292h3qlhEOamvkCZCLqjHl1JGXVJvbX5DbhbVX7sXF8uRW19JtaOFjxJ5hLxyh//YgGWEkkRvvZk9lTNcRZBH/QFNqGsUFFFTr67GWB3y/LGC3OUP7dfMLPhiA3Vf4PoFohieyrc5nlr8xN32MTsP6n0x5Qk51JJkrWelLhqX3W+/iTusqx+bj/MbDf1s2qLsYdnILXzShwcgW8zWsMaEXz7aShUKq2nXyzQTMPKklVe4IYDY80KhFYlHVs8EQJuLaa8419arjwXVFIdTvi1k/Xj9TwS7tgWBKmvpbu6u8Hd1dRVJ1+dtqFdY/BS7nGgdENHNsvsOY7DLoFlH6edlvVbn6Xe0UyztxabmDqG/3iW6TrGNJyyn7IgxhkZEr2GbdU+MQ/aYEz7CeB0nutXW70/XdCQ3AlbExvHwOE3cD6/Oj77Gta13Mlqbe6x68A93dxGL3YuWxneyKTfgm3V18ra9XbbG4kj8VsvL+gHpD+PvnZHiqQmnklvB7PW8uDiEwapMTMPdQWSpi0rp+oMsiS1mOkZbF2+uopmyv4bGoOTm9v2GVjTCI7/Vi8fq6kW8iaTvYd5iGF/YVU1skXAaDIch6WrDWeza/fnQ5KgTN1/rBTU25MQw2uEfzvavN77aa70pn4ZRd79hv0uhefU3xDLVgjpeP/k3/ITenlH60hqm0xcB7EErQ3Snh1GGMk8lElwaRhghliXFGED5q4kiP+/kYxkapkBNBp7upvi9/YZL/Mv61WX+F7Liq472w6OcWsrt3ShBNlHwhZO0c7HrL9vrbTTRY0Yy2jgaqY6XVLvBpizXtfW5DpvXFvI8asj4P9QhRim5ArUcbZoZc58yY2T3A4eQ78NSDFO1tMVMrD8m5/3mMtYXQdOAb/NKQe2V0kAK89R5szqX8fn0/d189FGc9MufjWuViWURHKCTD+GfLuqZjYirnH10Fs6mb1il7cyrUMWzdtLivJ9zTz/ihwNPosIvEstINFaF7mv8OkLtyzc7oYYarkH6+jRrSQyW9AbbwMM1qnFK+WyXerP57IUO/Nq8fBeQ3zRu9mvr0aX67M6Vd35BupVNHjMtf/9Wofwbn6/1EsPTTvC+wUuwvs5Z9ZO1tFQNyC3IxWzKvDHgjCLNfAjeqCwYvuP0sk0637gur5edvd9KdKdYz1/Js9OMVPtSlgmWJliRgXr/WIofcm0+5CA3uSmSViuGV/HcZ0Ec/trTsqnXXupL+WWe3AcsoDiBbw+6TVbr/mUyrhXBkwjmXcfn6/eImRY+oz7Pw1/dtLT+9vbGqoOMUGK0JB1BElJkdXbLw9ziMeJMLrBL1bOtzXpF6V36m9d+njrzFeQf9vgK/dAhPS8uxqP+YIEVkUgQJF5YLDOqlmgnfzaOilV8A4ZydW17gCzH4b4j1HpodJY1dR6VAGhqEPIwcU1bd5VgRXCC1E2W2J8TiOiXt8mswBjRFG/yBXPNV2HSMlm/hOVMFuvh0lhfTJIbSxWMPHl2OXfWasYlw0madBjdg5BW7iFml7ZuysHSdmi2XUy31ZWUA2avweiqAlax9dmNKz8lwfkfFjOF3GaoOnTpAzxzM4SZG/R0hPzETmePvgf9ohh3fA+C9xFOHj8nv+gKxc5iK/w4ToYexSxaeHod+KjbJEc/5Z4IYH6t0szUz5QbuvjkpggwYwFRv6E337lvjLd9OfT5Aa8NvjhvSiZ59qcQU/b20/SGLXWQl99scpVmdD8pNrSE/ae0RChWM7/M20WKYfQWKrVdq3o5H1Xx5l6SkFbDXDM9qxP3Z0OrQ0Qapn3Pu5mdG9wb0xHEoUaW8Qw+XWquBJL5KKiUg5j9GevPyzBb73I1W9lJIuPLSqRNm3CBIY0HSk1owR0vngZFcd5U/ry/n+JwNr5VhFabymiqxgk7NfGu19Nu/Wi76NV2M36c7SLlyPJWUdnS9aEA1lf0s3qfplifQEqnrndYShzhZN8nerInfaoH9Z092kAQHsWiG6LDe/iWyB3RMY9WFw7TVBi3hFuz+uA2q02WzNODDsVj4ly4puEyS/kqN3N3hnflGJxy3/jZfUZjN06vF8dfvTXlyMELazmrX1kFVpd8+MXuohmXRHIwT0mjpZliXdC5WArwPZvwVlWMWCd4kniIDwfrQd/PyxUaUa20w8ZtyiF4iZ2Ub+GABFkjeA1ChWg1ROUbzNsZvnb7zrqfjfT9e6w6JQFzzGW3P72sUvu+O+XNQcw85hz7liGGb9NV+J6sMB/cttVT7YR3fD60x5XOJ35mrpd2PKcy7yQzlPQ9mq+ivgSmuCtOO3wIpMmsi8StD7WEh8vWxPN3DkdehRXtx8Eh9Y+cY0xqk3n8C+1/y9+7NkGfePzGM5G3hv1dVIe9sD293u/MKo8k2/YgBxRBzCFhbjEkcdho6Q9qx3/yZIs1RJFoiFbW58/A4VF2JKdvsFL9Dv4onH5VbJ749DGnW//yqEOgtRUHvMz6on6ExQ5LVEulHu6a2eAWJp5iu44eIqZ4AB7v4brdo54HCQD3n+gRJ7DzQQUOM1FP9vCiHRfGU9RDUOe18mnWcPj7bO+n8xEzuDvaGlF1vSeddPVOb6tNIVL2ygvqDzK+3t1tS+v7afFE8Z8MH9NzuXZfG10of+NK8UAZSTAVQjXRKHdpzcMNV9CKMy7UBsT3tgzMLjsBDSrGpRbN5XhYlMMY3jNbWI7gIUGJyVDHuU7n7ws45cNG2F/E56l2raUkb2/d8pRBV12d1K/Go9QVseifwQbTNqUCsuTRr81HUwuRsABExA1VszDTiNZIzSuFePCl5H3eR9XpbGwxMPscHqCuZEItysuhcWd3vGNHH+/jAqxPLrsdh+l2vOYnb2tjbmPO13vM0baXT7qHDgvub+Ha1fwuCrcFz3LXnXNGjtFEB6wZb6OH+RTUceevjBXmWo84J9A5XxNdhDNX9vaNaXRlQ1yFjH70hghY98ZXZtuSfiYhSPED0Pu5RH5anf3D4JRHv0HfZXjtelLbxVrmrGbQFlBV6ndpouC5dKG6NeY3rJrzWP+YJyqkfS7mkcIy4Gc5y/D9ndR1qF3OGMmeG+F7wJgfik7tUHt/bbld2r4v3WFdClBIEOr9gI4VIdA5pHX39UjI1dZO3xdpd0CP09vEO4PvyOXdnddMiz0f5mtpWwio3pwLWg31etFZz4E5dAeYl6VdJS7MO1rz32XuOrNdqFzuyfV6cXZxT9EtwGJmicjP89Q7/NY3wQvVznc72Cee+BdKtZTPbx8UrH/RkbDlFpSOLu8LZin7cIsB32nTfYmOunB2urrQ3wNpBxoe7EyXf59yRySKtB5kij4u75rcxFkJaF+WjV/kBKk5vooPRptosRXmedViWXqUjzOxudbvJ1fl09yzG603c6jezsdex7nDrcKDuWm/vtpIKWhDB3yIOl9eMLRQflqrH2kLkGU9nfLue/rar418s9br8JCds6JUeJuUad7YJb/JZemwnB/n9m5FXKCSP8XQbJVX+u3Oxqe2E3RY1u1+zFvQE8dmmavAcz7tGzOhmlFIfbzQTDtrj/9xE0lhCLzo95kdvDQTW9SDXRBuA56PcCtkLNUQ1WyFrKcr9Un/zrcq35F8nj1qQPgvoq3yD6gPDLfBb4YdplBB+tdJwxPMH5TL9nt9TZclVNhJlKFSZz7ea2EP7+4f6yPHbzB95kspwrf7w6pwiSyKUQVMcWg++Te0xe5avobv41NqLyyyrmnfIq8ypaCIr+OZWVgR8heI1beEohIufy9kRXIUpc3b4Du3UCphBR+vJUw2cW4rK6joTfcM2Td2q/KNftxdh5uAxtDfvD+Ze3Z259FCRzioj7HRkGCq62JjHIEFEk+8xKI6ke5ERFO9n81ORWlg65y34r1ZPsujbYj869eLeyxA6ErDaaV976W28FTLSHQB/OwlVZbEvZaHpBqTLA+sbqbvGbLzxFqOmHm8Ja11FIZ2EdLTVsWeRVqeLHlbdm5+xBMYveW8Dmf9DXXfGyffGlIlbaoi0khDIB29eamGwekmrMzuOls9nGXVcuLKkI+29MYfJxWZfWaRSxz4OPgXzoZRngLbcL3FF2thC+Toopxd9ZLUgNQdmidlS3l1iDcg+oy9GBTz9zKUBjOOG+PgzX8zcEwLGLNZb17CX9qsk/enphJ2YKYpVd8PJ2ATkSY+hefycjjdRKzR6Av+Ua1Ld7Pm8Bq9eJdvHW6+i9QDrcom0/hiFcUYB2wfl4DKrNIT/VW0sbqTdGxWB2dW1Ry6HHU1slMn7vc/Kk/wzNmK7yejHVcW/8oDeyz3a/8ShOLjHkI6yfGxAjbkcW+MG+y5VL7KR0M316LnK6jATdNUv+P1h5uftQrbcKt5kjPyb+nsetoMqogJ6vv0nuPVj2z11rR647BrXXt9u82683EldxUgBm3cnq8YM3l9gd6nL77+ek3KDapOwfb9WWm7zcOFna+wcgoY9VOmUCV7qXcd1EuUwSBYt16/0bSuRMvUS4SBe5C+wdKRone9D6FDhiGeTQS1SLhS5fw2FcXywKva2z95L666HGOd16GkMdRfpjqacKdZCxIyzcUFWQsX+fcu6raG9XUd/TkXVMd7PN6XX37Z/usuinbJ7uJzs1tLP3JquXTN0nOrWNXz3RpHKT8t3OsZMjW0IlupZyWHSl1cGFmjISVk9Elzehu7uUtaj+zyRy4nXaFm1mPxWIf7iPN07xyD8NbPM31WZ6Sw5nNBs9Q6t4B+I3Y6M0l503JWfPoG7/RLGKFjiqZ5K/lQGIdV+3YNkluMbJaJnHjBHq2sU2s8Fc/eSru3pxvyzys+ZjWu19LZ9rR90l3zy3UDkrlha3+E/6rYGo6GNnuiI0n7wfrV+ZvdTu7uPqbb/YO/uDe9wTgi3K1OGfJgrQqzSdtCBTW9H5ejLTdyutxjNZqbP0ughW3mNvcHd7Eb65ZJQLyHH0ZRLaN6k0VLJJVltNBw2zOVD1qBOb3ki2o0uzNEfUAYw2kE+tHu5nyPpGs9fyevUaVWdJPxtjo97Lbr8d2ecu2UpYc2eQe3lQa1ELNFahya8zjnpcVVBWsg8f2hF/Py/FX55YYr1gUuI4p2J2nAShD4OcO7+R5XZy/nz2CQ23pKsJBOm5fJAwg65KBjifVU9ScMP5/za1N+AgvdAerPjc0DbsA9dN1dchhczcSUrHFHL1i/0C9CP3ymrZEI/7ZhJ5ZlttVSzdDh+YCO5Z6+2J7v33G14veP7uIIXrXll4qtro2KVLJm6Of1x28b6H63HdSFvX96/IV/lNnl+ntdl9k2axdXEuuq3ive1YDF7pB/yqEScdzTer5m6bBKzItmATxv5SOh7d7DARxu9yH4JUO0+sFF/IERJhoXGmTALxNIzFSCnEeNMy8XUQ06H8kbtus/irvuN7s6XOJvBm1eP2aXcEtOvZL7SMkauIi/L+9tl8i+lvxZGjkOGBdUIBFt4W73UnvTyi8pHPTPdw3xgfdeKwNU5Xm7HtTyWhdrrRx+Bry5uRC/9al9XAdgqOrd4LOPtxmHy0A7DSlL0iG9FYrr64f5M/XES8Kc2XZ84lnXrYvz2umkyv0j2PJF6XLl895p7va0/hQU+TKWqtMogLVnpV7zxkfj69szYJiSdLhFQHYrGmPulvPdQcxWO6i7L6w34RLfObiqWl+zniJXqPRtN+UGPE1rU9UMGzBDbxE7nLFdqSB1CXZ6vsfTMAD1mx2MCj45iGPwiS9A4syeKiOc+3XLLckGRtGmW+a0k8qPojOM/d9qs+Dscj57LOZAmcQEAR4Ti2Jm/pHQaGQezfFcupr3vw/IdlmHp/P3QPBlrjINlpPa47vKAdbFMWgzC07UjCOWFNs5eRJ9mVW/Q/xsd1L07C0DPeNP9XF/ilkvhwXLNkU69JkyPlUIPQ2sa1Wd0xlRIvEIiMa0Ia3gucFfwlk2qnx4r3/UGSV6/NbT7kFPq7eV0Hskvg2PfUAqaTitANEYGqEBW7q3rPtbtBUwaSzCGjKHVMwrUiMivMgVXizaOS/S97uVN0MBkVLmGGAdD4qsLmP6rYh5tpBN/CP3IQIriHNCIu1SdeHMf1unkL0lXZGGpAQew5NG98B8YnruG411t7qA74rIf7Dd6ul1972qBKGo18Eh0krkl5oaqnDLtaLYzlhdEU2nmr2NjpW23xDMvRhS7gh/7Mp/rF6d3a4YNX5AmlxTIy3zbA40lYfUd42U/MoQSVISlGXb0Btlu52VbpoQ7W7esEwOJ306ScLHHaZ6k0jW0TgdAlujT015BscG83ylBKU3hh0WsXS/P2fEW594M1ZRBkm72sMC9glZ8/j8qtPB5+mX2uFxJxaQN82ZOMVYS9Kb2oDvQxG54/UvvsTGUIV1IVO3AT7tOhQHCNM9fZSB8sWF2B2m/jpEm/fMcfqSYcV9TLG3Ko70MRFtopuTFTY+dKx+yjI5/4pVnOiEZLY7qNdUSO/Q68g83H4CzdA6TTrpwjSH0+8SWYadvbG+aMvPA1D/wPKcOFbb92RoVkmwBXSkgT/9mP4vNZ37HKCLuRfh1cgngdHbome66wJrtFZpE8LDI2g3lkEtSIwDdjrUurazRM6FfM2iJRKbxYCponYnN+D0NZo6pR1wpqpxYpfwQG2idDacco550S+geQN7pn3OkEfGy/E06MznBcsC20K1QUgNCKgH3rend0T6zm8/rXZe/r3T4XccwZreiad35Omdv3Hq6Z0UxnHQ3zvZuAXR5N0CWie36Ik3eV7Wix0+S1X8WK83xmTx1Em6mSs03yBfVWBh0k4i3WWkdiyfQ2hFy76Ejgrh4YuMzgF5xXFPNMSkxHhou7W35XLxFHIJBbcbsXN3tshN3aqAl193gyogKVFESpOqKEis21cC3qBcn3Np5xM6BiEfvAjoFIrnPPLSala7wNEvhnKFaAFyvZEksZpD+MpgKR1aRgF8zgbbDB15xehRte47meMOELnl2ZV0FxHp4aRQwxr+ikz7pYF1qSJZ5MIac/uPWZvHgXa3Ai5NJUMiR8yn35B49hTReYOP1pnmHczPrvsbcgF5wg5rUxd1Ru9Ra+U0i0Yk/eOaYb+nNG4YM52//Yv3itx0s3W42fg2Wc4yuuONh54c2Y2eO+r2xYreTL0gHSw2d6t8Bk/UycEZpJ+ZE8ujXXFU+tmqgaXfULddv3p2Cb6d2m/aEfzhaU/08gnW+pWMV6ei4lrX76SQyUW5g6PKYl3dgvfiq29W5Ja2egbhPtPX3OrvWxS1zsDCDR84G1fSaXjaXZdFUyW6RSIm23gYXjA3QV+G+/Qf5OBMtCcmTxPiFKp6feRq/Dd7Ge9tkjeuisPEyUToLXInFvKmvqmYQn1r/sHaSkvwijwcWEaQT6IJ4jUIWHWFz36PJb3IZrdgs8vFwCDdCQ+rCO94AgpStFCuIUVICetzCUimBatqWRC5JAve8uif/POKrkGMjN9AY8M8vKvcnZws/CE/DStGvRJ/Z2ilgwJH0I/7upLAKv1EPsBX0uewpdYttJwyAh2Qv9Tagn/bbA2pPGv7lUoyutHV02ckK+6qOJGyh1MolzDIwjqezvL2RqkUIage6lObpidI6XlBwmPP8bZooHMnNGZJpVbGOjtJBLSl5ZTreod+IIf1GnONAPb79lti0LSnFW8wIJ6ei0jfkNHYY2mVozkXatBDdRfS3HORecxFZAxg/b8bWP5d4Hv1dyjVmXYJScdOxE82Kl7sofZ8eRk+mxjzZ04/XJz5rjcI9UudBqYqagrSCnd+2tf7VLccX0Tp8qXzzyagq0WhxTqEWxBnhuUpv8zfaidwP6MpXyBTZFr19qeH9A7YogU9pkMtO/Nl4uB9DqyXZdNbIKLzARE/DFKKU1NvlrP5QvUUfJO8wrwNweMN6KkgM1P+7CP5Jhp2ki/wt+OlI7SnLzuGizqstZmY2+LXVo8ZfyM9ZYWm0Bm9yEfgA5eVPguxRmBIxJ7hKyuTUJ86mFMWXLaHaIJz2iteCiIeiuxUamnR5GlkfMu/Pr47UI6EWdmW/fBCvnjfrtev4kOb0HRSMtH+Cfpn7lOMb6ES+ellZRQN087O1okPO/JcXq+wLAI6HjYY8R7xyb6VrrzsZ1TGqMOpWFEGM76gg+amvGPubqu5x2z6MaX3XJl27MVD1NwuNbS6EnIMWKv9+rWRfi+yKxg5sK7nC6vG6OQa3UPQo3aYPoRWvg2n4VU8bMJTc2U2sq7W6+d6PHFgnZNmc2SKy89X394Osw+RsQ7SWMQR+mxyV/TF7cqn3ZpnMqu/Cf4QnzMwOQ0l9ZZYdDd8h1UZRfgt6PHsRWKLMejqTrWs655aXJAXXtYr8B07h/VxhZYl9TpsjXJqqa3jgxA7J+ENo1xiAm1Zy2uZRQGipzm0/gpsH7GKzOFssr3lmG0E6e7Y5tyKWCRVTHdJwWVlCp49k3KqG4tHwrIN+u0+8Ewk7Y1nArXlF5gR7gatobsnylP2LOkefknNZYjPt9yCtRviWOBPojqy7FtiyQT5iiU4VqIw4wbjhd8Op/JD7xdKJq7xJNxFXfuXELhBJ82vr08pMW4g/lmsbekF/V4f0OUDZJwhl5fy1c13J4x1KbmcJrr5Mou8Z2wsoZaOy+KSXrqECHJ1qvisSjMSNxxd9K3SLZDiDNXln7PsDBYbjZk7Z2HUIZ5ke0vDzl/THhte0WLZFTTcuKCTLKd1lSRCCdblfxwfS5Yj5TVLmZ51pM/YWQsv8BCWq64CVJcjdR2OF3SYnpt8t5Kwgl58mB4e7k6Wcl3hQi+Tt5+S9Vj3p/k2xEv+nY+L+PzGGPS+2boOqfFPqz1Mye1eocG5h0FqHN6l4OtbHvNsLCb3X1oZRrxONSeuspN+19eXVUbt29eQVC2qthNXFfU/5Q+mYyNrff84v62hXiASt7/J6CM4Z2r7nviVJ3ynfPn7Bh0Dg6MrQotNuMyErJDJ8J3OTznwvL8jcdW1neQy1rgus+eyHYntirYH5DSZ5fCLlyTa6wfRX8W5i5TRRDSSLhdYZVakDPfP+XVq/XcgYFjv8SGnskz8X8L6GzaYQEsf8bN4H17AljynN/jHKmyCrqrGvshj/kXfFT7p37syfhdLe59Sbqf/8dKmlOWyc/FJIfp7LbJnhCQJkGEPpQvHMSI215HZWZFTFHPMsiz74R1z0R7xc/zSl/ANJfceWSeJ6eG5QPICbkv9ashSIgk2R8riYK2R2SR0BAdS6/4l7cuox0r6qbJssIsAHqR773JsztLMV4ikgFcuo7S2/DBAy+2iBI/rmivpSvLtRAHau8YUEJ2VrAC/LHgZwTNUsR3csoxHwFDVASz3K+KDQdJ3Xl44Y6NQnoO8D7MsiPmKh/s8b2WtLoRG2kO/xJfVoEtI9Wtf+6MUWz7n84aEmwtGICtx8Df7Ui0xPtrefGGhFx9Db2vzxgoroEFiMW34Tu9IV2vH2DhCVRyKZXTQ1n93R/irpQ0NOxunEa2LhxGhC+7zZpn9+sX8vPBNtZrPttWCQ6alPtLBuoh66HZCn58evrzLT/WYiG7nRPxOqc1O8dU5iqJX+5Rnqi//MOUa+GSEC+K3rbd5V0W45AOxilJ3fb0e8fCKzPiMogrj6ZoM80or1d4WLo752JMGNeDZlml5FE9/MP79jBxnwc9vWnlKZfTF4PT0+97n1FIDtTJZJXj03A+GoBd6AQp5v/ytuNONyo00MCj3UyJUlUVuV9v777rsl15KTiP3dIPV03g33zK8BNbHSpVf5PvPUacdG27HUeKORKbJaDuVQ6n2n+Pmft/job3ue0h9Cza+d5aLfvG7k0fMYuoIi47OpzWcVHiZ7ZgHPUbX+G8HrV7XREVE01fB1rz8rNshKry7EeiivLT99fURzR2yIOk67SVvivUmWRVa7JP4XPNb+AjaYQYRcRFomUmJgJv6M+fq+Ruk2o887eCnAZr1AZ4wWIzvPq10/epodzzk3IzZ8qQ1Zw7vilmEJiKupozejILVb1KF336ulZ4yXKVLTwp7UbnTJSFTMoICtK9PkyZ5kWcXQyP6ivSsGCLKnRXMayQrNoW3s5QP3oObSdReszZYBYhEEZzFZuG2w30wRONS+894s/hlfcIfeiKBsZRnvbCRPryPp+9qqe+Dz1dUs8dgFvo36bnt37P76Vm5rzPusPDcjRg6GMgG22vSAykFTaQc7+rGYXWrXgWTRhi214tFwM8LoE3Ff7yaK80lz13WmREZVvx1lnAWWnpaCd8oPyvb4tQ6955RsCya07mnqvZR2+XJWsjl1t1aH2ieJhyeESmrBv7s23SKYmfoa/nQsZNoigudk4n0/UIr+ctJ+64TEbwJZXYm3qDXFQXkXUHXGbU/8eQWMDm7LV797/rbmKl51+dcgRXC7/e+OpOsRTxHY5rjznReELXicsXget021YeHHJ232j09Dktvx6Uibac+n2g0tvb15nLs74Xx+/FhsHLx/dctbS9fvO99Rp8RGbZAUtb0O02rSedKNXtH4CcVXk32mYW02+RVbigmhQB1q4KYqwGCuJbBZtXX0XN2ueit8+IPWbAmCVrzht4aNOPYWWtYTl1P+nNTYLgHXpLt+clvDpvuYu2Oo7m2aeczaaFzdKNypDROCaPcn/fVqrmeQgdDxujFF8n9s9cv7nwD8Xzz71sN6PqTqY8vko8LJa7afqUH8mVrG/33yi3WHw6sqIeFQOv4BAXtZYd0ST7pe7xNWH1f5INfdKvb96fbe3M7i5Tid9Z6t+rvEp2hITO84+vH4/femufQT7PTOeV/G+/OG7uldW4/67fbOr+LqskH107f9qnBYJG8r9+nejviJ8CUyi8hCB2uKn/WsuB8OD2as7znwcRtpL/kNnt7bVQsb9sr0NZ385lfrMVxt9TofEyD+3dhIkyZv/1d9v/9L38def9+FhN/nn6cMvyrJUSpIv3/3/m/9w44xDLCKyrz/GqXlTyKXnGO/QzO/GfNe6Vvr/V6L87r+QC5IgIY8vN+9tMeCxffJXE7X8qBcpAP6mrfdG0ENiITNaBVjXPSlpZ0dMfEW2/SJMgCPQDjyJK7L9dG4bm6K5yJEFGjmaTFV9JCt9Duiy2YgeQLQPX72/YEhzxVybQSYGlVWB8uj4D0lupyQfi3Hb2Af9tVCeb0IF1Lq9Xt8Tz4FHK0EJ92StfIKjuocFocaB0BIaoBlPAuB2XpUHPfCAap0mOOM/Exi3l0Acb6KJT7scx5zOvRg1VGUutPsim42zX43Twx1AWki68HbF5t7zrUXmb1/K6VwRz8NpJesAqZOha1v2St30eNP3XSxeCA7Uvq9bdO+zEXVhp84lDM69WWI3adK+tia39UQxf5gmsX8pLDKgfsnPdJ034TCYZ0ulbNNwW2vVWJj7T0yZmrYXE8tVbtJUv0yQwdD5db6+vlkvRXX1yN+5RkNkbTKF9xttxVMnHa5N/hqAPHkh7LSx2fjxjcLq7rGT7uHymU5jtvqiro1WfRcqX+WwGvdZo4AARuwbav+e4OSsG8nMuaid9d/H4QbOBb7F0E767mgdKrvfG1f/VOADnBO/043kesXxARd0qPo7PaQDke5OgVq4iRzJbHh3xVPjcILVzfuBt3WjQmWJ22h268bzpxUOH1rbTlSR4tkyyleXcwBPgUWrFbxy1XJMVYSHkE8/adg+f8PN3hRH67V+e1uNjWJL6H3SMxBMxRBV5/6eQmIO8YS8/xPN3jpGwLnvkL8aw41uzZE2ZZrtehPycxyvPveXMYd/i/n/QMli+BfG81WyYHvJZHJWCQ/wttUmVe6E40tNacFT4EF4AwvFvNAN5oybzq6zSYlyfQpJXNV/nYPRWyBuMK5VutNyV08AEheDAx4eDpDg8woNfxIQEPzem6S35H6dx3QArbT0PuOyZVWfls1d2C3DpV/PDEY3GPr6RuMPh1Q53Vn5jNFeUzx8onRE0dyz53xuICOX+iIPglyeqcabsDV+f7gNjB7ac0KM2C1bDWiQVQFt7k/hn4pC7+0jN4snMhi/N95yFwrE5nOmwbCITPldKLUMwdkySM76tr+oAbiDKxLNoh0W0N9GE2yZWMp4pvsSdQM0YZsY40HzIGZAnRT7z+xviSRzdg7ydj1CzEgo0h8z4ZQ0FvNkZFxrDRHk2/k5pOgm+WpL7GTp4h8rs4TGM5+HIKvsLW92U94GNExw4CB5HfTSB2ldaV3bX+rFiO5++5TzDf+ykbh5RQ1XdnZxFgLq61BtGAzDq+0+jh/aAH4Fvu5qfDFcPD+Srwugc1TAtxxHZ745OdKFc7jtCIS/4eS7TiKTI7wktC44movY6raXyH8CPcUC5MRHWiC8X1Irp1ZYF8McFKK8Ejh5JVepN5cu9s8UmPw+1V5tX76cAfVYzVSFEhKZmwzLlw8SBivusoCnd12jkEeckdD4I4cMqQPxRMY8ePkG+hB/vWs2LfAru4CuVJ89CnX1N1ZoX0nhX5mtDQrwnFnml6+BH8NbPI6Col8gqsEsoz4VfeOkdLwmc+7VOdCIMInuwuL6mPWHhN3zzo+BtPic3Y+BujJmPYf2OEOvM3HekYwzTGl3FvqOYAY/SIjqHLc/pNubBgDAw8p99PMvsmOUajSNgYwzTGF8EYggoHjEjV+vyfAJ6rYGhYt5F8r6O0SgAfsbRUKyWAuGinni/np+g8u8NGcg86IZFn3bjzaIFl2eMM1i74Yk5xe4xYHVIXLkOJGtGmNGHAZSBeAgwE8L2SeglNvxNOJ6A9oVhpKVKPSH6S6Wmaho2lpfjEUyCLXzPtLrd0vHgVrfOcTO4prujumIUEu4WBLqVt90mPUBfXoqgLuYu1K9gUU2h9WuzqWQ7423ZxYTSvU82I3SJZWDA+gxzWHRoDCJCsA/0QOIuYnm0lZE4rROeUmxk9EF3UyNh2aZBXdPEwaV9Rr2C+Id4pP8F8sac8C4n8DchEgGkhcjduzN+wIJSlPempwN1IWKVgchOQS0+d3o2p7ChHvnYujuxrbFew5q4TGuIq+jW8NKZ8wtvADCD+SmcWv1sRMi0oycns2M4wdxHqDMx6fcGfuLwtJpP/QYUiajvGgbE2L1CKEG28x/iROGQWzGhCxHEvABEJdAw8j+c0Bv44llXTGKXA2m8bUELfR6kpvNotZqCrudqrMT0kXZBF5i2sEJ56KAj7Tit3ccSrN/UXIRI7rN+YtSM0ZMBo4XTC2bSPil7jl/FA7ISNjp0wyuCEE8wiyQdEQaPUhR/h8KGD847eUb1mYwxkDGE9jRFOY2h0DO9vDH0aI4QxOBRPZMi0fqzIAqV68BFCIb7NKDUWDbx9WMhRyn7mjGuaRpnLT/TGkyV7UueUP+TCE5OD+szZbXsbtKYYvlWGLphIZ/wvN54Gm9MRJIYJiyCWCJpJgBzgABWq8kmjh2ychFpf0LW7BC8MGA9YP1FmCT7K9/ELeuyFXx5AkaVWP4HdBLXfyKQ3sFMvl25ouEIqYyyoU0ryEDdZWrfbtQKzFNj9w1TNrJQC9xBRmGPSopstXCJmW+elHnboLsv0/m1kjnnUhAEWi6ULYvby62QPP8OJas8bu3/cg1kSEohkw1sqLXR2A9c6rS+DXIGIjkPp01k7wDYny+i1w6cnQXAdNd1rzIbvezzcJvnGZG6CekpdunQDSjCLPZ2bMNkwhfwCbFF+snVK6qTvqzqfy+9JFoto7TMLop088E1+/o2hvaYxXjCGPY0hT7IYj/GEMbppjHCywoR6lsuFEExjYHzMrJQ6luuBPo2ha0yy6cIMqMcomE/F/pP3+RVYqSFPYwzTGAOMUWO6YGNcpzE8GCN6Fv40xnYag4cxtkVIxxD/NQY+x1zIGOcR46CjNBGjlWDY6C4wm9pq0smNQJNR3ekJPV3TYKdrClleFvk+pzIr156MiYK9BW/7REP/2JrxI4b+VPJpjmsmWbF8w9hIn2gLcz3EqtbO5LIUZ2xK4nKy94miKZwmerewNksjQ5AVw/Pd9Dwo72eZmhGs+8N5FvzRebWn24jmxnvhcgv9jfUMJ2b31envOos/uW/km/y6xVj5tQqpcArpUlPKEe7TLDw5w9gQemyz0RmvRtYQ4+X8ocZcm007k1QhCtdGTPmGLkgiY38LA5/wvdAZz0Ie401IKvC3rGQ64VzimFdAehZYgl8MxoN0dJu+tTaaXP7B9tNv6QzP4Rtj4nm9DSbX/m4Wln/f21y2qou1SPeB75RMz8VyikXMdsr9EmQQ1F1/ThvZbZxyd1ZRHQeUjlB3g1L7JIYGc0N4MBg/umfyx1Nz5ziJd/aL5cn5tJip4gel/m+IiFl6OgWfXipzTFaiPwt0JM98MS3ZSKIvMz6PzNIPZKuYKPYlswgyopWpvoqmMUo2hkg0mUjukHgVz8eBRD//WRtzB4PpWikBRgbz2ZFU19aYzq/WaxuZaNPlRbJnuBcJw+Si0aWLGLpFioT/h733WlKdWcIFn2bfnhAg3GXJe+Qw4g4JEEIIJ4Qknn4qy7B6rX3GxJyZiZiI5t+xV7dalM1K82VWppN3axrdFXnIh3q4o8Glp0L7XlHLtsVUtmlCJbT6BhvAz5so7/2RW4P/wxQ6c4acqbX7GGILmG/uxwnL4FnZeMwHAGMrzfBKJE8X6w9BMu3YConVKqneenTNg0Huf7Zq2yyOznh7Qn2MRvdZt7q9t8KJWBNR0ru0XkLVplhJ8pWt1p5bbDs7Q9EltTuQQE+oEu3XEMOdhaFmzlEUoe2HKNa7RrpBPEC+XI7ST+QX8djF/F941pPrPZsF/cZUPcE+vB+zTd1LtF+6ZpK59rwhVIDtMXEfirV5X8S+UtdglboisfDTA7nk4TnlDuAMUxaOcSDn3mTs3t9Fg8w0MdAmJFwKNTpb9sEz3kdYx0nAZpyap/IOefPs/qNIeGdvKc3jZxoa2cXcDV+vc2PprYzlRl7eZzOj7yTIDA+Iw2DnDHdlR+jLfdMs6Ity6rU2WqZ5D+7eaIxOb+TS7JzHmkbd59m2TsafWYViNNF7y5ocfeRWPdYez+VsJj3cVb2DWLsvIiVdbsNGnA3ubywYiiMWaXe/deGqj+XagKNsokF1mZFIp8BjdXekqtcktZDx2x2UiRxfwJawILJvmiYkE4jJ9J9cxczsutyoTp9IrnKRLHVxmM9gve1ShCtLi0NLMgRTbqU4gL0/C6ewCw1zvy1SscI1v759B/SG+yIaDdbOcESpiVlhitnLO8jo5AklssyiWMQBZI0QCBO3j+VOzQ7P7vDZER+gbzIQ7+RSLMnARjuWXOIJwsQ1b6bjI5lr8Xw3XSd4oxWYs8/4bLW+7qZFXji1uxoG8h2f7H6gwI4ooJC796yACDALnf5CQBMI5oyxVrfD0vYFg2u2oHWunNE2XSdGWJNejt9ewJsaFMfz0soXeDgEmn2OMzzv2GyXA9VQmjfJv8zshF47x/smC2epZyE9lFTtutFTwUVY4XEqsUie491HnnggXU2VSuzcbAfpMZuYhViIj8Edf0/B3xsO05Z8L31P5uv6szgTU1c6UoRKcaHLGp8dZ1HPNjcsogvJMCl6JTN5gNXURO2tW+42s89BKkExyJhiYDMr6tbJO90n7OcqtIqpNnLvfBajNVgSWr8P3dACpMY1iSe8Z1q7rX7ifYVnmrxgpo5qXYeLtCYj/jzHamfYoOwSD3WgMPwJbXahdL/ivbY9JPdWcnKnw3augIHjQi3tLn7IbyUBTBTVTA7Kr7UwWun1cwauoG13Q2usbMfX5uCULpK38rQST+J78HQ/N4HUT4yJLQ1VjVav+We6RsnxhQ2rsSpEi8NIhABRa2lafp2I+XVLcDuN4V3LxHH3M7LniZcvWqy0ZHNA7TMsApMwFQTF1F/HZCR489uK+iTUGa0yIhmbbeAuw1DZTrtyDAeR3HRcyK7kntPBbDQ+6CKJNZozpG6lnmu0QstgFwAvJ+Is8NaMKyzjY4uW43SfSL2yCRvVxSow0xOYc143sXhzMrxJ29DCo70PxshcxkRe+mjENG5AcsUMT9INJkmLlTSqheSLmclyVi3W2FzQvaVRZqKQ6wwzMIolq2ONf8pQcdH26gIves80DKgWELEsTneshUtnTRobC8fEdjiNdxZdFn2GOU+DopOeLfXSbR+5jqjuuGd+xkTfF6ooFVtpri7uLiqYDpnoTK/1k8rHXYTXrXRXMDFKTJdEDMtbSHlyihBWl46hcv8grlPaaEQtU2x5CqJkq9seVlt6YCWIPac2g5zvh+Yq1y3XKIPJspVtqvdKucuwGanwDig66xMV5hB95yAxb3diSIV2kwoFk9ri/GMOSsiQrOScwhyaOcxB/s5BTajdpktCcjqg5NRmoXKLv3PQUc+s68oc4Tm46QLmgOfG5oAqi92lzwCHw8aPLsQTD/Sy7yjLgt5OxJsGuElxxAoywPPLw3cclnmmRw6Nk1OFkleHz/FFNfx6YETSlNkgkUU5R75OyDlLrojenhwz2yQCIyAHHzLFckzpRdt3TILK0AWFPLrFgtKXUoPthe2KEeuj//YhktGLFetjyvroWR8c4blJJ2Zb3Ggfekb7kL99FKSPBZp6nda9Lk83pvwCCYzT2Bb1VA1yrXzKvgn1nEn8SR4yCiiKA0FyA2b5PNia3qQH6d0uZDoahVmV9o0gvDcV8d+pN8iWdjmZ6Y2aMrY0Yejcg3jZZCzIGIqssttmJwBJAHgmXSi5wXThkQzoMsoDhgcfTHbrSqIYGBRrZzfpdGaNHQiipoOxRV80ea4QCwH6iMU9HVai7ul3vSIElDFHzELVJT9g4yNoookOfFwBW6sFa4vjOjd5z1Fui7QlZdQLoSo+A80CVyDj8Ck6oaCA4WyuRGExeiIxZ+c4tS4TDNnmOLUccpya9HHDvbLft8ya17E4xK2DFDTpiWKIf7HuseRbmAcda2fEU03H0PExOHo13e660UkZd+eAUDPFiTnWhucLJ8JENV0LTOgs7iYyW6CsL2YfWXyPTILZRxxPklicGQG/gdQS3of77QNOhAnpLlkfAm2zJ31IAG7S33kfCu1jYjLbU/n24ZI+/KRgfTC9Qc5Vk8xjoy6J1wclLG5qKZ0IBp637Hc5Z5BcaAN4qUqM1mRgiQzQJvR8u7A+7G8fN9yHjWmHodJnhirZxFsCh5HudZTTvb5pNuz1d34Pi3lR5BsgOuiL3MiWzGP0CQrNkRvw9jBMNMW2HErIYMnvUkv7yFFGEO2QI9pyxnG/K6xVbjJEjawNuxPRY9Ma0xXHwPYcHTeSgmDG7+BkvkSHjWHPx/AOYAzocEwKU5ivin3F4qZt5hEkRK9iQ0HlAdwSxz8dQNBcyDpIf09ZJFFgA4KGdWC2PwHELLNv4wMHrUkJB7ETDmLb0IEGDjv6JscKz6oZyIG0olQAqiL18SGbgthCwBpYFLyPEvpYSgHvY/vtA0BcnfchffuwwEESSFveR880I6zGh6QPhqgGhs1R4Aj6KHkfFqr5fSUP5qFyVAaVMcO3tqQP+UxPNMScsD406MPBXIL3seN9rKEPQa55H69vHzAPCdzkZB7fPjTSB5ryeXTfeSxJHyXfEUPnfYTQxwwxjEvj85DoPCymxSGF6aZYW8QGBbpga4y9p0TaNPLJBeijXF7UYBVQFFyCqARZ4z6WAfPNIm0J+/ZFR75RbhoawFZWzGeLvCVHTVTAl6ccb1PY+iCUYjNnfr6dR9VkvrHwEuQc+fYzjng14FMuZeY0cdEXPTuDpwNilBmO7fI4OHcWhMhsEY/7RjUDWaRDJCGrxcyP9T/lXpOpmgdyhhBHgg4dwy4XnQWLpbB1RyjjcUUZaUsSeVtzfpLnagBtOYyzogOPgVsUW4Kzt2wdsJZ54j8Ag5Ikum6oWwDngSgdsKmN8O2sAg/r1ZtAat1OjVZYMVHHDlYCoweeV3NGeK/doHfV4/UE9qj/SUbpfvr8TO9XqfLMYkWDgkwB8k4SfrCGoAkiKzqV8blWhah4KeEzMjjFGGobyCl+ia10xONf5QwiQQAAoBWrFeZTchUL+uByingQKMxF/LuRf5q/sWU2Gt9I+wkac/TcAl5756izxSwHrJEuAT2XuSeCa6rwA5ZXDVpyZFvmXgBs0qiQm5Q917/PyfsnvtNLJltzGT9yga/SrkWOxRo6Ztlyyp9rTFfDP2CqdnKTo9fuks9xDV73Awge4oeRQqaToRB82FmS8DGNv7gq8SYMLYaQD7nupaMYiy0sengfHAfGr+A+NC6rvB/rhOV6wuWnqXgcIfdA57TNL2jq8j1UWthD5n0AI47JnQU6Q9/LnMf8ihcGKBONPZFMjmEeOIYpqms5WmA6YW05f9oq8vKMzw1ryxYL3paN91VUmWxCShaym7mtipW4TP7ux0Fi92+Q/Dax7pVyGlmghnOEkbnMVSjdyXDrhp9iQ9pA/y4/xUqv8uj1E/GXzjjNzy48FtnMotC1h5xODJVVNnWBdvRWYdwFmZwjSEQCOmqN2Ht3Pq4rSMCsDpivtrW/+DPuQ7XXfLw29y5ZZ9mbsvN/XCbiJOtICfJ1bjNfOFbpCnaTHD8zzUrb8VW/c2+zLmGFtgDVj7/HNkpOwFUaFwum6UjMqywRH6/y5zmPP8AWC4qMMmHIOtAUHTGWu+UIcxXmQRB42DkgY41V8ucX7llYg9u8ECvmp8ntb0Q51nLM2CcXKmdyD1iDR2o7QkJXJnew+AuiaPb1PugX7n1YW7dTmbBTgpyELSM2SPFp8LifTEdPZv+iV2GbW13l3x9yCk7UBisGTJOFQjTCt82qkEOX6QQoYPIa6a6LZd6drz3gX4hXJ4VfTDwufoK+lhNUF9TNrfw9QWLOvNtIE/NiEaq8veZPa41pnsuAnyAHADT647OQzbHJT5DUIhZbhbwOqW65l9lBEaU1P2WtjE/dplTpfiJpxNlKrtRBaJgaU2XQiHuysRyOJMyd+Qny8RqwMR4g2EBisWBwc4dpwWiBCcvSUq4/tEj/M0bVKFdfLzc+bPxHLIebOOD+tyzgeQjwj6E81vj6W/xku+Cq177rX/1Zrwqvvcv1qFaN+Alv1aVRDvgEFYbtIvAS5cEiXLK+sW7J+8Y/htaY64NYZLA9wMZyKP15bv55HobKZMksQ5RzSSKhsnDaJ+/6xH2wsuSqfvngc1j/mQM+LsWsYOcXSwnGNTHXwlr/pmcbayKNc/lE1eXenHF/fZ7wMWnnZFzqJR+Tx6MmTOSlWG2WC9pUInuMJpWkX1jSnXkgwUDnfSuRsA7RjR4RXTpzOhhq+Qzc2FSHkVg7cOU3sBnPQvOvjiZNy/bwlTqi9O0XazauVjLNAdPQh3PRVj/sDfVPW3DDec3idOC6z2Cm/RmvUyT8fDReoeRCzve19TgpGPIgHU0hC9S87rr7q7v2cI+d/nGE7T16OpRsuqiMXtmfzxBxaujaHCKkj/3IzXbv7wyU7+lCm7jTSnjHc48TegVMevvgIPGH547egjvqdUdniA5/l6GW0OCUVT+fIJvHDbMPtiz+VJMmfZr/RsSvv9RIP+af+yX0A46Rv6otc8uBf+SAoWffT3376wVEXvgZde/m/0TdS1tOHeyTR5xzss+WnwL+gtz+U5UbXf56Acn9v+t1Of2prgyfXvrnNoDGJQVvQnL+VBYnbZT8BLEXsPr+95KX8vrnC5jC7N89+TOu3z358fndkx+f3z35Oa7fPfnx+d2TH5/fPfk5rt89+fH53ZMfn989+Tmu3z358fndkx+f3z35Oa7fPfnx+d2TH5/fPfk5rt89+fH5X9kT0ZoPp8yfF3iQlQrSQwjm3qS5bZBWF38ttYMu/7SV0ozN3/G11vKfObNsz9/xyqt/9ua3j98+fvv47eO3j98+fvv47eO3j/8f9iHtD+5zubj14EGmrUXL1SK0x3Jimv8ZKf8ZSf8ZDv8zFN6H5+vQwZPhcEAf3XfPw/XFHk3g2Uj9z0iuOv1wqw6vZ4/fYd+Y/Q+RuMPhaz37xvR/sAdtsX+d6EORPTodivz0+vvZrqa/59/WoQwm7RPu/nby4XLhQyA/D4Vizzqbs1nsLs2BPqIP6ld/YQ/y5625/+9ONrtdr4fstUv568L/ZBH+D9dAnAl/LcBEEP5r/gP+zs8F4M/+VxaAD/evBZhccA/SvnhD45civ5I/TB7NDZ5neF6Q4pn/jn/K4d9FmOD3/zPS2P9YM5CLFlqiL/3fbdzc4z8UL1g2d3fd5YcKVpc1lj7/bf7fTv/ZUryj1/0BlmCA/9yeitchuu8y+Gv73N3xs9OrurA/80E+6cr/X6T7+f98z/8/2dPB/zlR16fdHX4sKryWeFIwnSLbXZxderj4t7p4FTeYdHp7vW4VfuECf5B2WZmTxZNvl9uTNDU6ks+PNhBbsNcNVnJX3/H5wL8eiw6WXCJdIv5U4E/wz/vda/efEaK/DrX7Nf/PUC5WmLJawdZJxTvkRcuTCjVV5BK43l2Sad2zszFak7Sm8kYy1xv8oyyeEcSwtZp4j+dwCTpHKsSah+Kw+eyCQMAGuaK6LizoWPCN8pLKo+c7VX7yQlUJy/Ynt5Rv7n/hANu/cQD3v3CA7X/hAP3fOMD2v3CA/l8cIPwHB+j/xQHCf7CZ/l9sRvovbOb8NzYj/Rc2c/4vbEb9G5tR/gub0f/GZpT/wmb0/8Jm3L+xGf2/sBn3v7CZ8G9s5ndP/nx+9+THg989+fHgd09+Pvndkx8Pfvfkx4PfPfn55HdPfjz43ZMfD3735OeT3z358eB3T348+N2Tn09+9+THg989+fHgd09+Pvndkx8Pfvfkx4PfPfn55HdPfjz43ZMfD3735OeT3z358eD/1T1R8np0HEX6+LBqbf08ySrtc0DqXSjcdQZXs5+t8gge44e0Nje1ITx1+aZtz7a6R0/dMLXLyS69vE5NbY+kWRx8ot1Eh1S5x89ndPXL2SrKq4uIAjHx1s+MVqyHVMP/xNr+G8Mqmf/G697/jYP1zH/jS+//xtK+zH9iVLX7P/G4/74iB//G9Eb/xsrawT/0rET/xtvugn/W34n+idmVdv/GJ//7SvlvPK16+ofm5fLfmFy8qn+fG738J67334WX0Lr8Jzb4d29+9+b/ib1ZQLKtM3tJd71JM5rt1GzeHLdTKHIa+bQi7aiFjA9e+vQm6ZCWAm4dqNkx841zOHBZB5N+1EyOoRt/4P29v4Uk3M+3cFR78SQ6yY0upSx+KeBz3UHBzWseq1BQdDGz4CsqJMiXe+vwzamxUXk+Dak020OwJ3WpyHJ/+Mrk6t4zVOUA6UvJuyrLiYs/0/Pqg4yKVG2Cz4FnhUSK2NemNNaGf/oq/+Tu6HLHbFpSwwxyfmz41gaL+q/vuDxnjRHU60AaX3qe84Tnv5P9Mv8xbvtP3pGJWnTINxuWRhDteRZ//KMtjdGoWJs014m84HmIbo6yQHVBax/ij4DJmw1tOTBKdNhmOaN+m2dBC2RV7hGtJ4g3AXfC09kEea70Dl8b+cTyFakSVAeWoOx1tOltyKIbBeMOKp7BX5HEZxAt8iBwRPHoiF25q03aA8p51mNMekVukZoQdGP4OYP8pvVpS/JvIpJClM9CiJE8ttXv93OevUXKy7wiuSbJiuh8928wOzv5zi7g9yn0gpRW46vrqQFfXUct5YrkQYXBooQlEZJyFDjmMme77vzJ7vPBS7re3vjKrr9ZhzBRmk5dFGyMOudfOt79umj4/BK05RmbkhzJ0ZpT1o8sPlARrw9ctaS/toivodlhCiy3fMddmeWqQ5BAVrG04NuWzNva4Fd7fBpoW1Ir8basTkJupfK2si/1QMYixToj1paBdN6WT9oyv20hngNMak+5HZxynk3pm/1HatSlErl/Ejud+B6qqhz1t6Ooqf5BRzRL1p/aHS0q6/gQZjHhN9uQ9j/n+WxUeSNZaGmyfbvpL76mE/Mml9/+NIHxUCWQArU0Qo3SrYdWfE53TMCTbZ6zLF8NHwPc3pG3M0ZL6BazGiCQxzFyCsZFsDTh92ssfILMKOb7bEMqRTJW6YHKQqxklsFM4veCsOzBx08Ickoy6snjmcrs3DIrmicX6JsnjcMyQkYnr2RnSzJDPiYP04qDEk7f3xxsOiSYs3dmwtfpm21sh9dJ3eaME+nFn2xoOZ5GMOCZhjyeEbKWc8nCcpvRdy7zsyUlJ/yFntORo95YjjlIGSrBF+i8VZ5bUE5OAYoaWrEMzsea8xDreTxML3O1w+zKtNUrp6ctz1eoo55zo8OmtU1nNlCgaAYbe/SnxgtWc00xONGcjq38ZUJumCPrqqKAjfHG13YDKb5WHVJZHRte1wxpHRZtlRtIrC2N50iSszhAVvxtS9L+5Mpr8V//tLX+5ikkGVnrb1sqrwagZFGLLO/PuBTe1hHSf8WhydviuR6x0IKsqCTXJPxKErVxbUeyLDnkq1Jw+WOQnLNLzponPGuwDRm2i5ynXwvsA1ej9tC5AblW6fdZfUclgLykgqxRKlHRlY/2BFvJuZC35JoTOUxHldYZRZq85LWQVKSWrc05naLx0yef8D/3zlNZDSReXd1UMMNylRWntv6747BdthuzHS+5bJdRiKIYjTk3c79ZPCPIrP1RRTomS+bZFPMEqm1Aumq6TDuepXOHW1YfaDztHSg3vp58DpVLGZcuhExXE3A3luD0o9nhIo+l5kz/Xii8dQStGwGbsezxaoAOVEHdygeT5cDkWc9yBNUnDJtUJSXvMdmlIMKiDwl7z+J6qlRB6rSJSbPC4zNT8dydMvyQ0hzRcA4rXr0MQRY/pSEVGhFkkfv2UUCmN5KllvCizmX6M01Aq2dRSOsRuTznZYYg+5y0gUJ99FzxCpOkWB1ugukOWmfynK0StAUZ6mhbGa+3lGFtwAKRK9C2rlwrYvXuZN6W0vE8121nPKZQjM2KkV/tdwGmVUbXrTLkJ5fUmtxZjMMu0ILXY4PnEVS0oiPmGscE6iqaUC2G0DWas7pzARQkRC47UqbUUE1ERk+oTRmdFox+BV5fbA9Et5SulH4l5PFs1XoJnHlC60pjugZhQDYNyMAgOSDh+Y3fftVkUqfVKJi4cW+cAkntG1HuWZZ95PJ8zRXk4LNFpqFgi4vphDZQxVpaMm77taJKXyhWd6gFDLH6obQySc3YiuvWAtcRSUZbLV/d6BhNxKwfD6ukuG8sEeh6KiGrUYawWMbnVBVCuuOKx62/OIT0iErM1yfj6yO5wEKCgkmTHa/0qhUkR+HyxCjBrHm+ZlKfJ5Arug466ljti3wFuSzlE+Mtyjf/9ofUFZT2/KTVPGen5ZLhDpim1i6iHzm+QboMeR/9tw/Icbkgiir5fc9sl0SCKpFKrpjsd14PY4FI7kQRPdjv0oNVFaDZeXVJzdnvCbNNRLmFtJh5wNZd1DveFslem6g73lbJ2yKVEXRJ4m1xaSKCdMf0zJPYZvKM7qGPSB5NQWHFOP2v5hSo0JYsr16d0SXPPRh664Tww5zxlQWtegGVkD5L/H+b5Dy7D+jOvtnOCjIsohQUMv8OzTSduyKmKh21TAcXZKafGqEM+Q33kKE6oIeL7LgqkfK5N33BaysgxnOJ9EFGWxU017/McscHZ8i0uJAOjPJvEeNhIanTIC2oXSCjgOl4JdZL1RzvAFtxm9e8VKsX1HZurupRfZCF8hhnxPYTowlzjIXYqIzV8ySRMGd9BLxRVrnXIkk1sUgi9Cchk+3zjvQq5S7j9BHv1RRI/UJPdelKYIOJrQStX+gKbAxcm1CgeHaJ5TWruCHdCrZTGqkdgoVoR+WnwnK2tyrNtrxlWmqiarza8RbO3U3uKF3IHatXiI8R5Cs1QkXlv5e0akdH5qfsqWRQsEHGK1qopA2J8bjKYtaDmpAcuyKUSKR9Il65glQYWPAzgZKS5Z59kozKyvdM6CbL/SqLpCxGIkes/grPPassSDJiHbI70xc59cakLYkUPCUEZbrfaiK0LZ22pRQSa6shVTlsFLL1/bY1BX0mVwJuzRoy1WhzROskR2x9RYlVzfUEkvMe6zFURrHMzYGbkQY0Wof4j+Xj3M4wppvMkoJzy8fD/8EyW0JAaOFr+SgXklN3UVCL/I/lcwNNFA4BtRrkfMEwgjE5FUrO52Ax3ctWXZKD1+qodbWEMiI5VOyj6aUziemJMrMA1JNDMpTaLh2TJe9YbSVaAEDBTVPSYIiXZAZkTPhsEA0Fy4ETq7NDqp1ItNoI/ixPlG4FVv/So7UIsIW0oINWQgOLyKB/YHYElWm1+0KanLtrNKHrqTALySL1hm7j5JV012wWeKP3gR0UVkNHK9Yuiq5le37re2Wu+PuPCB241CpTUAJV6+x1pmV+gtdtPqW6eVJ1dOzD5HLECuXYvorjqRlu1KO+XwZ2OZ+9Tta0HclEN2E5a3U0hIqMy3IHuVifm8P0dlTOJ6yXNfHQjKxDWIcOXTXpw2y2dkHKJVexf509oNJrQE63glJ28mI1wTaPYt+RUZah0J8wD74hi1V6YDUpFXky36S4U0u2DoYyPVbohHZPVy/V/RB/BRu82IKg8hpbV3TuOyA6e1nrJToMY00hNWtPVIt0FWZ0aWBA2UvhEqDDOr4qQNP2iVpNrsRekrIYj2WpPmN0iEIjjqAOQ45Ycc5VR/WkTElgCPdH4Jjm53XH2jxWsNesAsu3Lhaparm8mAaMfSD9HDtYc3TsN8gPba+XciH7U7s9wd81dj4XVklp26gxwWm2HQ6Q8bqcAqiiEt0oPfvoxvRJYrxdPGuFjEt5b4UTeUmiu/BmVuFRxrxXLi6TVul7U0zBal2apAo1ym1WlwzqKryCIjc6cfxPO1uB1ng7w1lYYj3QmoyQmOGxWEwxZbVkPWox2HiNJqO0gMo2WF5SS0RissMIVTgfRS71I0kY/6SIwGR2oTVIgB6yEXA7KN6wOAzhOEGG+JUinWYLkhu4pPXdGJcozrVq7GaFeHnHgHUg5UrrOTxbCwzG/XNudOZ7v31vdJuw2xvlYFI4OKB0apGKqV4+RJD3usc/T2MTvihtZvWrqTcnWhcxXtLVyF8pDDJdmAdp2GD9rbRuUr2bZgvga/1iOir88yGF/Oz2ySDroynu4Qly3DiRmtNBhV64rxSujm1JX9ZlXpjnazMbPV4rUtljyWsHbp3R/t12fhJ4pZUo7muzMID5bP00JhhWQC59DVjNG3utQ1HUJ7YlwzHmI2qGqs0bP1IAF3HPD2f69iaPJcmvPJYPrNIgluUAHzrO2jmNZRWbdgJoV9PP4aG5sI+LgNK6elpI+K/nXO7nF/EANFWag2sN5GCZdK/laDhqxsewjn0r1zF3l/tnJ8xhz/EQOrLnukUrM9rxGu4Ljj6FXzxBIYxbZTufspZdqM0xjXfPkdgSsWb0tDJn0LxhQS8XFRYUqmHeqnwRD6dk9NeguWbhbN2P1qlA+F2uMRDdrWr4Yo01Ki0Nje6p5xK2EpRekMQj9Ck4NutzKgqkT4316dI+qxX0qbcR1oPCwHAW/mlO+nw8oWHv0Ww6ot3LtCq0LIfBJ+6OfYLFUCBZ3rGZ32t8imxhrHqq769ULwdLBIoa0Hrm8v2tmHKupzOdUMxIH6w/wjrY6A88o9E2XW2xzdOOzddkh7uO37fS1Fh91iykFVKL42p47FyoFTwUImS+Pmhr3oHJC4GDufdr4fhAb+eYcCmBVtmRgxbbucfOhO+JnX/BS7t207QwlHmZXmCrOuBuiranp+y0K6C6o+zNPsEQVkkp5jKmkPbiHLtJ5GBOYZXOfepK1Vnei1lrBKCD78aMc4iDHkna2x36Z0j9rWXJbbdQRni3G6ieMru+0QouUZrKkKHxu9d82myEGHZhfFNbvVVLJ541Q4SPUoxG/vRmg9tJVVLfFDzQTt+nvY7ljTMU5vn7oy7WTWylpqSNpBy0ADQc0MXbeSfwUq3VzWwkY8bf4O0av2Z2MM0DOZYHCpydG61X+2K+R8FRBvv37GFeRQG+I8vyfW69TiApWgvKAw7x7j0JLcl5vJ3qUKh3571w1/J51gbD8Sbv9SHmi11NRtPw0VjhtQ40fdyapX8cLnTX1rBc6Q6hmBI8KRoTGfVy35qaSF2DJGs/HO8Lvz1hytjKrrJ6n6+zD/WqCdQKUYJnjQ1SbK/ndjgFCpgD4nS6hb64zNVaOQrEBmBc0omdV4gN0DbPxWSKuegKM4trPh0SPRSO58E4d1BYlGRlF6wzrIZgvscnbFtcQX3wADUxFotq1L0XZmNWJtT4FPUIztknebq0fqyyoQLpJgzcMyajrTyWWuuUYZE07S1aTRrWPiLrmLghRT0vBBG6LvbtO5wBnegc6+tBC4kA/bGzqV2GdNcyumt2ihU6GcDNm6wWAcOr3RPHjtAES2tX8STLledSQXv/1rLWsFqoa1+/hMu0Q0zPoKAIIUMLA7fhyJAQ5Mg6hx73ExQcBo46DwVnm1e72NFaeVBtAFKDfZEerGzw6gD4C+iqcXx2bTF01pTHmPbwF1iXah3w2t8e1tf2asIx4OxP7W+sRmwFho+hVuKeRt0FCPrIcTv36yqQzvAVBQo/EUMfaXweA+DrVRbytizuw7EyQDk2HP1wEbNEsKAG+/nG2zKIZ5r85Q5t3V2GAUp/YHZzFmIV6sMxwAyJvK0RjKv409YXZt+3ZZ7vEu7bELllmvuza46iHffRiVLOlwXbcujr03vxih0+1NcLI44ZqszSwpYO1hXMGymlBidAYcurQhEGyZJIvRfS45/q8g9wCmy418gPeS2Z4NJCBXvmBbSRzxHWA9QDcTI+iwfH2AayjiSjQAyTz/mqB4YIOuiHU8mOY2qLAM8iEnOGd934bsBooQYHUvlZMJg0R0oJ+lhFak2SwVw5qom/AjqtwncKv3njmHaGv8LxIGy28+1YDMZaB6wb0MRhwYanFynHsY0D1uUeR+A0wmtQzb18gl4582V5t29RqFEjoNl91WEO9VqdlLHfGV27pJ1cijWvQ7XAe5PX+0EFVZZjgSEaDtekFdgksCnUDZbc7xEf75bj/4sE7N9bxE50LbW8ihexKa/ch+F2HINsCQhvZ9z3wK0mdFZngNGqfE/GvM6IVPhIssE+YP4Nl2Gu2EbB9imWb7ytM9/3jSpiywIMYOqvEL+YYufjUUHRR9ZWxnHLLMYcqJW/HoINb8snbeGpsbbGzPZBHm3L5N5qm7cFkV7Ylpa4f0X2eVtgztVIvaGK+yunrHIvsTTuUO2OUMq3etBNzwAwiPiZJi1QjkZOlo1U3r/KseoZfMPm3/BDNl5FwNqTE0hLegYE2eWY9hbacZhXMrdEavuYsg17qDLM9IbmHNuOgCQMhoVgHsXmjVYEA0cRf6/4YuAB/o4sMjJFD77nOsHAEcOTsCbKzmdpkvpPFfeZuRyXlyjmLXHMm8/BQz3FvJknTw9YvRtpZ5HCpozDmcjlFSwjOJseygPmU3BZ3wL1fnB3L1rzmlhWUgKUf2K0E6gNx6Mj+MLSZBxA57iUnJQt8JEb59QOx9uzTgIoxqA0pWCKYEUqiWNDCUy2R19hpFA8mI8JPLPAPvfT+diKu3ElZseTNzCwGeReyvXJv2nHsyCXnePFhzmWFauT3ed98JDH/VJ79rKhxaG7y/uHkiGILgFeBdiM6majj3Jd3o9XrBGIx9Q7rosJGJa+NicjhPJPSEdvhjqhRc15zprU8jP6FfOlJVxezki9NyUfMg8laYNQmPImyJ02dqkcl+ucxUkdsMSxkNxmHGWX13xJRqQ2r/4eLUNlPCrIeUGvjlfUnJEKXN5D9M0bhM/AOoVfFJ0j8kvYfVdZe6tV5zDPyrtMmP+tJbVipZJ7pHqLIaBm+awJPp5zXcWaUUozpXEjvPtdR6in1b/jLhlH/MRCq/R3u7LX2PLEOsSZnYaI8Vr3DKfEyjNGkW7F9KkxqbYludeC/858e55LkNotH6fv8rHcCfbuuNTniMfGazi/aCVNotlRlH3H+EOLyQ4g2XdpBqz1krXWkTpcSh8zOVrxCAiR1JGSRE6vMcdq3S2pt1XrTHOTclYJXnZzOBM6qI5k0duY4e06YEkQjsP4UMJtUkVywcOQy0wTc/GiEtEg5WtWVtIU6VyS71wQmYtUMj+zy+eivMjKmL3L5qLzuQx7Uis9WbC5KHwuypbgpq7CcGcvb5iEr0l9PF0pGJbVGsyvs5ZnZC4fxPB7h+GgDiZsmItVsrlIFZ/Li2y7MjtFdJcHJYvetGuCe4tmTeUJboP5NkSdQL2Kz/dyENLortbPQsCxZzJbfy3n2uPsDOtvyivuD8C2C1ka0avJmq3lFz0tRntk85wR/m3KHv/O7Psdl8wrtZaU9zkTj2HycPSwRMXnNacSXcl77nuSNIDg2x2TLPhVJWBeJvdKMH6NyWFf2rATW7HWblRyyrnJ6s2npBac5FIfpYwNY0oDrpSRuVulQGig1VrKdSysMsFXHJf6ZHWJVYGU3AuZp7RlWmyrMSl8Z/UL45buZ+VAPjVi0pmy1aivvmrCJehKuGMD/7tZuatB+Vj0aUkCMceD47pdgvy5s8iDdk9q+0ni6ZE8nLtb98/73lIqi/oF5JDpDO2KvYe19MflRLmui5g/yN3qBNOXa3podKWmc8GCEeaClHXE8Mo1y+isKzeyvynTWHNHpfqUItFKqF5eMET7TFE2Kd/Rc9aapmMCWoZ2ElA1D2N0PeF6vMyzAasK2O/M2LIgivU6o/TCXPqn5jIJr3N6bM0P/s8znwPShj4xj1MIMqqf5PSpECavZLCoJDDV0IDru5KweeK3sq1/l8baINbSGv8qAsjn+mdyNcCUTtX7PtjRYmmxgWriqwBUaisETRxtuxr3JyfHcyCR0fkRZmEZdPMyDvBPdX8kt7NKLO9Ps8/6/pHctfI6uzTbcw4bbGTbriStMAGteLvVuz4LnmnP5zt8HBPnVpGac0TlTx8n/K339NDXZMWWWoVV9KrB5CH1LrBjwI78ugK4FSvNSE/nw2oXzlb4d/DxppN7USyOMWhousri67pknWbXaCNuxfV8TyjB+MwAbcDfDHahP7rMEV4bV6M+6hwZz1hcz2LfeljO/EL2wZcHg0/lnOUGxmAP70W1OC6gn8W3n2ydHozoKCa4bX0ek56Uz+iViUXbzop0bGaetuomH1cHOqExwzJSo/Nzfo6tpoXYkAE5qQqKBrAUjoINNt86xqtlfdwmhBezaL9WrTZ757zI0rYlNftybQ1w1gXrNqpRHsePx8JfbCk1Uxu3VYvlPm3SrXUpj2IDtEN4o9xOX0CIxbDFzwq/H+OdjK+tQOUaOYuyG0lz5VodP6MOLUktSutFqEdLfXOPJbklqOQ9WiEem8LuY+IvR32a+/ilJakeKuVDUrBPe11c1ag2Y7u4F9fFE9b/bFKdPbHjSdruDtamGpzec8qHFEUdCOi6Xs4CYJraXjIuzx2sv89OpgnrvzcmqbhtMKtf5sTawlSjxZvAfNSOWeCfJwNBcvoW3u83hAvrCCKmCqV33SoFIhobz7m9d4xrNZdAB5HUp6guynM/U67PhSKmmANd35vW269P8H2FhSOgeAB0GWAu01dwqPLbCJrzakhYp02TpQ/9Sp6obOIAOES6nQxmznHdb8fK07of8xBW6PBPVvRWP8936K9o+/xp/vtO/s9NACX899bCrf8nil/d/nvjwP03Al//97aA8u/NBiyTf8fyPx+LPz7R2xqY/02tRhMw5UvN9l2KtgfFNQeA1s+fxt7yXynwo8HxuNgdHWDqk/l6GPUTcO+kj2chVNmeNGun7794JGpdcqY33fDcmdoQGL1d+ftJv7Kci60JddVvNuDmuGITSB9GnrbuPIneiDibMx6Vqd8fn/Q0PnficRQL3eu4Hp5fKbY8omI6nQNbkMbGKtb2WUVjmZQTjw5sze3cvIbZHKoLR72hzozKsd5TwbJYvHrNcUcH6fHQMcJwcu6mQvmctB+5nW8yAuPOx3vLuTePRll6dK7992bK5iHXQ2E9aQ31fZ4a53e1EfNbPbsW8visuSXFWf5Euc7QaKJ205FQrxfKcL7YD6OVn4vlNaIYDPLmPCYEVBHp4h82vWkcD9HbSez1yN5ML8/X9khrizaPyo6mdM1abk0j+TO/GMJBUjbrs7uA965y774ESzjYZkujib+3fhw/3Azn81IPQiHy42xsGGXvvqvJQ/Ry+krWMywHq9D987n/rH2hWeTEFae67siOrfn+4Q3w/rGRL3teiRqRb/RDv23Mc2w0WmyUE29j7xxzOPcouqQk/C6Cbi+v4sFXhn3zbB1vP451d4NuZ2qi5md+iG69NDuZQhAyDHjxvUu0uz+9937vGuckifxaHmyibBqHW83JaP7INlS/756f3kszat84Jm8kGp25HzzLAyh/eM1IPkuU/AnXrz9b//kEIi+6bvjE3C+7m7NC7O4vupJbhq8rKDxfB2qUOrstPeGpueQ49sBuXkMP7u6cxKEOwi0/fTLLb+7u+XMvHusxvQOQX3n8s91lR4gDG2ABo33ErjDb3Wg/PsuO/6GIK3rkzIzXCgs0IOO8XHVjZfM5nD69XqZHWlV2IV++EdLOAcs//Gp0Aq3AiFbjrWTk1ZIfiH7HEGBp24FmgaobA4D8vOEc5d08rdf+aKjxTJ70B5t+Q075fsKZD66zgxeW16GDtHdvroTgsE6IsxopY42vGLCd93N/Pvj47WfRIiW7H41l/2eO3+rW8hVrJdIe+de1XiNzZEeOCYoYZkf5UCK6JIBodATbx6Meip5gfWYHa191siJFK+G6KO/tYLXRngrlGWOOaX3cIn5AuLwPTXapZBXJfKQNPEqpNfe/bNm5L7v5/hiM3HYpHGKxmnl5hqilzssm7x6gZYw62xvdz5NZjerPMkxv+axuaRw0t9TxqdTtDJb6+ok26T0ms9qr9BS2zje2+QKXxAof1YO+lPEKxVSOtEd2CnKsGEzA35pvjp/bXD2g8fRtiX0Vvr50rXNsykXmZbOaXhai2nvHNVAQSINK7tVXTF41e4fhJ7mFD8ATaHf66vB+2h9t9XMmC5fF/mI73NsfsVGu7aYuWa+wNBk6IwXbvXC453W+2cy8Whu9y3Er4L14yZx/s71Q1G77EvZWfLdndZ6imwbztTd3FhOksKPZIploj9N3bIh9MNVyNXnZL2E13B2rgI4czAx+N+G1HDSDQ/Nyj0q/QJJUpNUouIbWZEv4kS5N+SkBvRVbVUb3VBIlbASwFCfLXnt96CL26013AD9hC8ahZIWqtuRrlc8ek80uAlH6fnhegKqQxQnWaEXtPxN5E+SbZR+9n4HaHjZ76/Py9tVo8GJ0OeA6wdNSkVopwxFHixx+A0RGXoiX+I1ChjyhKmfboMGN7itaMbsX/84i0eUWgV1qjS2G3LXml3YywJnb4DmrFuvhQ3wljErnBjurrZE9JqPlq9cgAl2ejy6gXArcuzDtbXpEjEL3b2gWHZ21nss1v8sjKjW/0wLfW2FLCiRUoUGU5eI1BKKPSmzBfZaEY0uzK4tTFNEbL5ctHudNhxcFq7t+2diVnbF7lEe4dEBj0z/98zaPrpkNdcFzfTiJjL089kw6F2N2ZTfExQhbcpilS5iwNEz9uoT8RzA1j85Oo8zG57TwTq1GmZtaCYjrC2WkDvk2bU7uKaUjRfweXAT0Vd7dMp0IUqtiOZHUp1ggERfoYwl0lFbxqqvxqOw+o/RTr9FaVtM7tsRWIEfJQNsVQzASK7Wb88Ddt6OhPazxoVU3k8Qxd2zmWb9gAnR3eXqNskdGuYP3VAiYuac1hP7M6U62Wss8BNJ2AuEgmNVd+8/phmKv25fNorLrsUs9xv0XC387VnPG1gd6Xs5KIFVj2dqrxuVOzqyu2Bzo3jqlrrdy1Mjgx0eoUnL+p/GzVvXcmXebmPCgllduSjSHzC47pZsE+eg+zRsdVtTKMybxW3m92U8fSwdrkgru/zgfNtFyvr+AfYY0znck/Tn5pPg8QVg1Pl7XZln1Ky3WloyX6W7F33zgN8OtE1v+ew1Y9rzQ9uriItCodO4UVtDECgfN+F1abjndtdj+fbWXxRqrR4zrtV7O1zO0D++h6wl3QiM17ui5HxvN3j09GS0zTAbzwwdxARzNug9fT+RKu1G0HO92z2Ig0VOsM9Qld8HqV2fR1MIrr2AZsVcIn7K/3Fbv+yrAXzHfn+0qW5s6WVW74B6OfBEs8bTSi/0a3UvrQLUBc8tuJiG3uLg5WivBEdS+V0CIqrUYPZuKlKFbicyPHKbP2NxhvY10y6lDSRYPbATUym0kL8dZfGPdsih9XX4COnY3MccflApS06tZX07Lbj2inFwechQ3VYGZyk1olVd8GvGKYp47Wh13Y5vEbUgvrtvrg+N6utQxM1ZGmHciAOpUQGNSI2a74XG/nT7YrKcbEpIxuMlITu/Wa7Aabtk6KFvuoa5DY7aaX6toME/wul+nr11lqx96xiquBXu7BuuYyl7bn6aDHG9UesbzCZfdhkkmZT1ZZpTeFkTW3MvzrMjzCDWbaj92HDyQPKDc8rVRaSUJB62JXFRqLXvNdlhOTt89o4gTpQiu5eXWksp3bCBWGcL6ge/c7f1gM7xHhJujFUOSkfyKcf9Nkyv9M16iXCVycORkK3e9ZWu64vIWiFZ9pEjJ9f0EmwzW/hII0MhsaM7oWmmTx7u9T3VsqyJfeo2K7XZ+nptUL0RFzDzk73vzrDEFayNrRTQnn0f0K3kzhvN0LH11IklIOkyfh8q+TallZbQ+86e0qb7ZG0UypnQv5eEOM3EF6Fsg2qOogNzxSKE1LJPw29ciHps9OLjkwfzuZZfrGttGS/p3dUJ3zxlb6/Qwneym8rESPPBl/EX1TsYiS6Tjap++gvRmzu7LAchqwPKU8gE+IMZ34Sa/9SCMbiE7ROY8bVfGp2Jot6Nom86VIVU6jjyrgl/UmOuDdqn1h62b7fBBWU/tg2MG7FwZ/eIxpTei3v0V68TH/WpfgIYA9278eLXOKuLyUkR+Fw0QyDdFTOvrp0G3FEnFYS5UC2LHBXQNAnZTA3ftUPtxWFf87ebRB47L1l5UGf2qF5BlHaXfAJ0qAca9gVNanJeE1rhvjcwe8N5jBNrQVI+3Gt6ArXqnlLM/A69uzX5Fkb5rs5UxRSrG5M8pU9CR6c2JnU4mfeyX076XctRmDUGDlT3e/5rsfyUxeMvtAryjzl4yyjMeU25WYvQ0gwAwLVeZAXKcE/quLGzzfL2KZQiePuf4spz3K362xL+8pWdBQWfM9Q/jZ7kXajw5f2DXVQ96vLAjLSHuF+zUdbq/Rvsx1h3lpLGEEkzz3TVBhEI+M9cHWDEVVoQnu1jSvQZeXXajDV7rFpslw0usFSHhP/mQIWyurl1GB/GySGamituF4Mu9twr7Lc2B8MWBYkqjj9g0DljeKO7IzMpQGKxG1o3M+V7SmDAkeZgTCcVoiTVi8GG541V5WyY0Fie9hh1d/xeZd1x+RPA0ygeijvVTEokqEY6SqsWW7GhrbgTgJ2l9oSQ46af7fitTLmhK90ymFW5iNdy+JlkYYF386A4WV5At+FMyTTFH5Y3ws7qPXw5ofKWYJVjlJ/LVNSZNlyfEqzLpffM1TogNCrGfFUhhstCuNma+yuJOtKpxez2s8fk20UZdumRPC6fcEb1C3d3OvYyfqzK2YeTpEPOOe07YQ1dXFnUgoj7uHfO4xvtvk4tYk+RxrqmiByGBlsp8oyF4GlrtMXew9P8QhQTTFfeLmhW2s+npWSsZiQ8G/yU94VIuWWO1VLAGgqWo7O1LckbUXGTZmvQ4DywsG1bxLSE+wnKDWOWhPgE+kyyapiz6HbZ9qeKAh632tO0bcYHISm3NyzWmk2dBqazid4hUVERwUyvUiEaBDwzFJHU8KejExJZpDr4eWeL3kXKTeKD1OGjZMKihnZtqAPt7Nk1KU+irXSimaapwvc9l3fIotVfZqpg226hia4WYrqj3IZmbFw/Lcb8jNO9wmpdS84bbswr1RfatJPv2ag4GBf1fU5tiAPoFi31im/SE4u2LOBrEWsnO2mzM8iJIrzXIyPK9IB7Uex1bzNt7YTIruSkTeSOn21ebwE1GqhfpyGHeutaM8AzA86hSSjsxSZ0gj+hgNXHNPL367JKx/fEbuzOR0EZl+cduS1vTuR/aAeByVwejSw78TqDUo3/AP6sdR/MB8bsg71Cee+ewBj39tZkNVqkqEDK0e4PTQoxtNj8pgZugsdPXw9YlNmxr3BitKeubM5Nnm70D8TSN0PQ7+1kIRPeQW56tZ62ohDSuIz0e2/K9uFDNul3c2KrIIFiK2r8Il+ETr2cr77N7XG+XOaUYMDnsM43zAZc3AolHJjR+htT+1vi6ogpr/WWCqQRzpnn5Bi6RugrdnR33o5sh8aMfxfUA85k90VRyi8VzuYpz3R2m4gk/tKbnDbY3wyFdtwuLK2vRW1h1EEDzAmoHCPw+n2JrbyYQEh8JMxaxgV+5Y/sqtvTZC3NbJI+Hq/3mlRPO6API4M8/x9GgJjR5fVQADbylEUjEWrZG0XH356Qqvc+sq8MDcvi8xpCiYQ271WFdhwljNFN6xv+kq43tY2d2jeCEPV1Y6PLM9tF36fovsJqP6R78PMGsT8QBxPKUn/1ltJ1Q6eFM9qxfcNhuqslydIN924/u28FhGBDrAbz6LOeKn4MRuStnzx5bsPhcgZtsup5mJuG8M4OhGX4BW3uODoRD64ryqZuJHyOyXUevJILwOFpRZMOfgEwB03vvgleu7lfrKFy5q9eE7k48cimnMzody9hplIlkzbVGuC5iSgRfi1+ZnAjuy3QA5BW9R7jDjeyEXwp0B6358NJMBYjT101rBBp3RfFF4hej+pGBta55gTcZZL+C2jtuyWDyAY+5efzNaVT5DfaAZYDRgAkKmxfZS1p2O4FoPreFRyXF4jy4A2afrTGfzJE2vRzijEzDV0wqU+Xgbn/SURQ7xCrFUm4lVIfD57ndPB+5JML1H2kCPl666/ymZFmEeH/Fy2FdW4m5bcrLZ2QMZlTJi8939NavT/BgeNNgNKuNaLMDNcX29xba+8fnQJy9tedV2Ob393S/Mjcxi7Vgd/blz2qevrItqI3r3E5u5j7WC+IbmR/n6/HjsVanLUBdi2t9CJXIuY7GLrY1Z8pyJK+24F3eBRCpr1QxjdRHT2uFFZJzf+/X2CR8mwBDd+Ione+9C357fAd16wmXMi79xD6i/EJui0yPBwB91yvnlgpzcntQ/lgs0uH1fsTtZBJPtVzRnWK6t41zQNrVU+O8uIsOHDTfzEexdFdRpJv+KF4ts4pGTI4Z2tau8Y4798UM84PWVfbjszTZRLsJQZj7we6otfuxIrvgvsqy4lBJt582xMei92dzrXwQvYYAEvhkO2kNmsM5tBM4KYdkWt+DulDmjlZmFqb7M+jkq1i7ExklJzrDvOuBv56u9cjvWyl1tVXTSF0VjSdvJbXulvhWBkYJrrDXZpWrw6mcWjlU9yLxNJTHufJ7Lq8mm2y3vNdSuZlkvkG8DffzxR0ewu4j9aP+iaRrd3s0TkDo0mWRc1iyEWSjNJeJMWzKewcnwC2uV0GfRgH4V3RnoajRGvzMeClu2OYgtwo+PAbFq6nP/XNysFE6LBfgpejMzVjQoqt/iKYvWAmSg8K8d0vlAe5x/17cmg9BolDG7oAlpxQ8llcCwmG+Cv6YqBPn3mqFFa0OIk4e3aSI7GyD+vkA1NcQ3dHsrzbevvgQmlrcBW/Rvx7v8bUTJsUiPRrpaxzr9lF3Q1F5zYkEJKJUcikKKSMAa/f3a/mK1mg7PVkGVuFB4koDQNfPzbAqetM3xZt6BR31ei/uCzoHeckyXSQvx2risTNL8muufz7qebiH8e+w3geRFqM0vobOWFWTyTQ8i9WzLuXZDGqnYX2eai2tHmHJ0wRjg1LWGDiTe06uo5WRrcwSc+XNZO2YzFJtQ6ZTWA+wPN1D3ffRC283/vYYwMHFYEC2LlEd/5252IbAzKSNxRPIvN3jnA8Z0sbCkMATSO09Z63WKDYwDZ+HhypZOH7rDIQlJimAZLB80IDVzEtqMcf8bLjlnZyNtg/JDDxrf9wMd85NBh/K5ygnhzvCZ2Ps12sUVtN+al2LpifxW/yaq+VHV9Ea6KlqfLzR1NLxKsKVk910Ib2nxe1R2Sd2klQemzxw1tPYlJVx8TzN3eNn3AnSaj5bNZPLot/0tjFu0SJ9jLG2H9cg/xW0YkhcZfYkIozYj1h8VNLw6s+ckR9nx/hWzuKn9Va3xarpIwcol/hlXLT8Ui7edQXv+qczW320H/v+qBuePq3YHG/mS7DKNSHq1OsGmAfkbOTfqOqBgnnAOtvmK/AXlhCL6g58tL5PrgfqsVksRO/0rkTvUNkldQu3HoszkPL5augoDvGhYBvtJnu+Vh+ftQATOjq2eZYgLG4UagSx0d0rCzgD2+dRgzCZBmrspVNAl1/RJd/Bs91tFGFinWBV5BPFW1vB9M5mLnG/bvI8ToRHXYuP4FGNi6m13loxigJ38qymgMzfZRTdm8MIcyR2Vn58N4WzspjVbeh2l2EkjALpri3uiXP2R6JbdgI29HK3Gq8wD4g+BEdqpe+sLzDrO8rzZPvxxedAsITANit1t7FUj966ZiQtDTZJ7ZWyLJ10UNgv7yAvkuielYCCuejGR2Ss9gW44HAfur774J8Oc1m2TvKlDxU4mwuGCGObB3qHJCVJPPUnd7d8TgSJQBA2QY3ULfWquNKtkcw0bAbz8r6o7MfYw3QwTotZUZhRl5VLcvpvzGJOlBgQc2t2qy+JBhLOUsp14Nr1FZx3xmQ5UeaJ67oyyOQUuFgOx1d2c8bFlhDbVSjgLMYy5TbSw2kem/eoAj4CYUHSRZ6YI0fVz661WnZLY0uoArk07g4LhZFWoVOhVX0MZLC7F2Ziqtfq8RCLj7W5W8dnoJ9r4B/HHhE5rYwFatZKEA2yGirI3guXBcgLN9i0Cjg/h+9NdA2mk07KP4dYGKw2Vk2wMJNHhuqnuW2o28HwLJfOwM9eHdG1cuJMH3fHe16f5ruZlG/icFUz/TNg/uzEU+zmgpWqtWDpdvQEWhZOkDOROGnxaMQ8Oyb2iTqUiwG1590LW7c2euJ1O1vT8Q616cXJCttEWjobeXNv/3mW6+EbmbCq84MATUr5DyQedZEheFk3uwSuEjVtkK5JFBgyH1PnVBgTQYu1Gzn3KDHZl/JBjM99Fe1Jjycra4JdqsvKpm1E59To4wEF/jSyP9/khm58nUUk7gsfDkPkGBt0WOwSKLa5n2yl9xMf8Mzzlt1WJBqobt1IlCLAV9ftDXIRFv1yVkS6lMLWTkEwg+a3OUQL8Y0GT+Exmacbhj0sqL2Jkosv7j7+fIee6cOpL9ayWxdbXTI3vV9b58oReu0kG1hxbJwd2SDUayxHhAReKdCv32rfJ+0oqW51CULC1BNXOjr9J5K2Oh65AbFJoweE4Go6jRCxV0NiGbSWnYVBZg9H8mbc4LmfgjzSHcwy71tLuTuzbY7bfWGrKJwQfCG3tZZWbJYVAfavmdvZJZGbDOWVOz80Rov5HERg3CX5VcyqPjDi1SarSISf9MpYNecATsUrF+8Ckm/z7bLbVNE7Co5PH7TXqTfysoOXNI9p+OyHgaHK5lZ8WHt5YL7Q+oIOqVU38sNfSTtBK9eGXi0Kzbu0u+WslHeJUTzzXA+l0VLvGwL2umJTRjowbn3YbGdqWMkkbBALksW0MIu0InPKJYZ/i95w0I6CWD3Ol6icPux0kqF3eZNWijUT49g8aGCmWZlvk7uGpzfF9JV8k26UvSSGL+GJoo3AAnRTW5bsW3/YCr3d4C8jJW4eH4q0434EYpyRTAf7Njstsv7qgd1njOpqUGOb6uEdUxfE12TnFOr80bpXbHWPY4vAesq7Z+duP3cXmBmoRy2Q48beFE4OIxgmF2klTz8HBRjdLNqg+NQejYxYRTNLdAkfNk7+86UrgF2fjaFy3CTAJWcRMtxun4/OMKDLo+mOA1PGChw+iMuEWMGj+n08vkeDVUegSLHZ7JLLyehGeA2W8cDo/FWloKAdgwxYVOJZvHdWu7JnYX1UTuRMgKIrMesu2ygvd9xp3aMNssFmMGuCZ4rXL3mn2tu/HZ51YySf84cg5TONjz73PfwdM3CzD7Ba/X0pJ2Vwzf1Bv1KiY3ecv07rml5ZfaQU48wtf3dFu/G5mF27HJ+06F7nC6zow3ey48f1tx5dY/k6ZmBj3Aw+umrIbtyrjymSXk97bL/fb/WoP6TTJzuNdsniRMhctKI9yyFipzvqUJBWbSk2FhnhswrRWrlux/6n0E5aZy93mSb5yXye9VQXvi8klUqi6SNGaPrwIpKrwjTVIcTGJl5PsOX9bq5fpQAkktA6a+LJyFcW04LVS/2sj9Mj3o827ozOWT9rpC7mj74/m7Cvq9yE+0+PrseE5hUdYVNZ1bk0uYm18dVakXx3vUkSNbRcWTobg4/WwWk9wwiu08B67zs1jB+CDn1bvG9PHSdBuZhcp3U1WbpI3U/TZ2UXTmM2WqCMmnZgnraLE2Q9WCjJldm99h5udh87pffMfIF3yUnTMIsU/S1VW+DjS0K86hQxnD1bxS/Xw5OL+00er8Rzcr4OP9k8QtZHWBySxjyLD8LYh5shNuZ1ukbr7zh1AZ/EQ3xbqK/Rwytc0VQbRfAeRzAZH7n6ti4jGjs5tZm9d25u5RsEiwqHfIRnZb89byegt7nogO1Oi74796tglAxn+2ElJT0ZqtzrhD6kjzY+x6ber/IUjl03jmLzFQZoOqw/p+Yj65JzEDF/hZfPNY2Oy3Wv04u4no5gRZsuHUxKJJ+V8TU+OIV9GCTy/Kj3xmPrEo0g+VCPrLiQM6zovTPY8OFnZeq5b9XdXdrMDpvlppJGr47etVbMHSEAw/WvmXQrpwv3GN4R2u6bbPBqNDeKa1H0KPqhFDrEQo8mk8nsQWan4OMrC4UIxucqt0bzhZ9616BFj+14Oi2c4tbZ+AwcuvRzI3u3kIMrzRAj3V6dgz63Nkehs7surOmzjevOruh4LHbzFh0+i2AUPZZF+94fIIW4U4fYCD90d7m9DGqLthoqNOh3el/lTjKsXupexztvB6jXhc/p3VnyvtgtqSwWbdUj51uprXvpyPFRFHLM4tDD9RPvk4WznRvVziql8Vn5knJttBivEnlo3nD/8qV7Tsby0DOH1patTp5w7v7Rq6BWlufIVGx96ctxJV4Ik5CQy1ZSXbfX3OkuEqp71Y3C+rNjMzfZ3SFAFQtPPSfFSS1bt02CmbNOC0rLz6Al5/WAdTfJl/awZ+5w6UIePZtIbem9ZVmURq/7aXEj6gI+L5WIV3ydLnTJHmYFxWXToUpPhvbOQjfaKr60ghZDM+gs/XMKPnibOxLrlFwYAnMYqTvL6DfgUnlP3W0wGkfoCetPsPPhluVBet9bXV7NrxLEXYmDedbV4Rxzp9VoINCRKlP3f2PvTdaVVZa10VuyAIVmUtc1KvQUFRFQFJHi6k9GJo455/736Z3nb53RWGuObwBZRUa8UT+pcnBYtAbjAo05MMtrsW9BA9wnfm+s1/qbetXbflZPHtsIfYAkDFG8FpHGXagI3pUqsrYaXiGI0b29jbCYhTm34ofSiZdiutS4MeHva34yMLZbObWnC8BPVgyFm8qF3lx3LLXj6Q23AyYmP3R8yvwDqtpyYw/SLHOFmHDuQ1ARGdCK7eNjS981Ye+thKx2RgdJpb+f/e0ZXd1npzekmo8ECyLvvzGiWMoHGeYBpm9mvWNbsVeYVcLd34V22yGCmYYlialsB4G+dwj7Ktf4rQj3PURrPHuQ5y3DfY3SPvqcvVPcxXTxiM2+OM55cKdjcm7kQL5itgPbKJniVjo+LkhDNT4pbgD7vfvUrtyXo/VI/9a3eKw5+fAe1AHkE+NgZv0tWf3BxghjLKbEe24siL59smneYQ6m0lj6OKxryCBMXofTw3kqmIus+XZ5DryxFOSAEXribzvdGqoXJUMZpPiIiNdaFjNxuB+Wkzxo67AUuJN+bnRqWQOmOmOj0ynfqyMs6RRvG/QUIu/A8aMvXUgJcO/2BgvMd7lc8jsqvL/H5PRyC48P8qaGyq/Kpzbxd40147pgotTPUZHW62qLhY2ykrJvTDwc6PNDg++pEG6naov3CuXepVcP7JLbx7rwcJbKTTvY9Hl+XhcX4ztVYHz0RP7E357BI3ChROs34TnuVoLVay0T+zDihxvZdU5areW7MMHN/FodkTpMwRRZVVuqdW7yjUqjkfeUD4E343Vb1dwaKdrVW8EyktjKBeaMpeJajuR8sOyVQmNdvn8yB5TG+zOf1LQA8zy3bva94K++Xtr2k720/5FunLSnd0SbrLcf2UsH889Fm2IZ5cAe4fVDLtjxqcL/xalE9ApunOuoeatae+kdvr3709PZ9Ya0kIThzbJ3PT8ZePSG1EcSuPscm3/YunFu1Bvt+6ktOJ3gu2XxDgDXuRXbY3I1O+K/MriESBkPbbeReA64QMPfu0UEDUDeOZbZbIL5vC/b25BWG5V+8xK96qb3fk94g4lkNC4x1jBze+I28rl5xoSuQSeYk+U0fqH38sQNzfWGOVppvxNQ8r+97H8ve6o7zrUM+edtU1sFaqvaeLzbPX/BYjF/bpXv9bnB1yfI0yahWF3p6N560x4WzX7w3o54b+1buzW6E+YGYX4r2SNzNboeMkzAZUh5voACOSQYRwY1cZfIyQXkVeWrKCz6O1PFatoFR1qPZDvrmXCD9vEb3a1p4/Q8ijJgWpdkxHJjn4DCqRyT3ZtITv8npcAncbutvTqVMAbbPVWkPsf5PheC5myahmBqJM4yV3/hK/sCRCiK9Sm/Ur6YPlRffC4lvXjUuzwAEXCBw+hnDldo6Gh43rB1uC2S4tOPm95tN7kzn1UtUXxBxSvKMVUdUZ0WXVDrSBaXmKrSIAu88I7V34miBvqsmFAdRhmaPGjXCUHjOZIr2d/5E6eWE0QeuqfCKcQsrXXtfW7ubkp8Z/msz2G01ZaZpy8pt9e8W1EPNWv3W4lElFGMZfOJuxj14lqJorq7mciWsru2nlRBvVa2OGkk4zieCBXibxubrBdYbuUPEKmzdoqs6EYzXWlTcr0QpiNElJtI06cJ0fjGim+POG6SPe9LMgETO3ZI5mK9f2mb8FlYzT7PSrx+I9/uVv4kT1QznAwa/d5r/cc4CuO79dVcXzti9vDCShwKbq0dSMT/6BPpYQtcF73c+o51PjVL8W1eVzcrX08EKS2pRBeTZ7Y0zOBsxpZWAUewcwldmYFvmRem9jBP7tsVUGEtVkQy51q9KRPRXIPcVzX89INfqebWbJaL+E+mcCub4BVb5t3wpezfK+0p9tL1I5XD6sybx/eR3OlEmzVG9ViDDC+kW5SX7zNYFpHFfvE9uhL8IIjhu3qOHIl4+s1dmzg7fnUaYBClS0H+hdN9XDzM2OimKTtfyc7pNEaj9yYmToWjhdkJ1ivt68l1C5s3GWOhwQmoN8rFJ/32bo+PpOFWOeqlZPHJ11D7GIOb3W9kdT61y1WVdGm3kYrzEasSYIO8Gb5+ppFjk04xcm9NjJ2i4zpheW7UJeQO3db47MgNdFhS52K0aZX+REyzYrGqNAJIOtAbnG0Z5PorpbFwvc5QauXIAfhqU3aYBZ6maDVFz8IrKiHdjR7QtS26M4fybsFCeGUT0TaOto0BhP8YvWLnHzkE8hz6CBCqQfbH9uLbvhte2eftQpkyxNZboz1cl11TkgRsCUXCb6/sKzIfDsst8V4hprSUL2gVoV/i1/FtYikv5iMa7V2jsxSq71EjqWDb3scYCSIUSifA0hxqxkq8RFWk12cqBTbWDfNhvR28Na6+wfoVyAQkK1clIIm/qKdyxIk8CTJEY1atplOYYc1nsMTjXb9dugGu66amUWUxHgDPoOhKBrmWOZz4JMiOnKxcSrJVL4MmtPs2Zjq7Rh7MONy5xi1bLh5YJ+I+g+GXx5jEjJ7nVb2Mb6SbD+ONpWEmvE5YiwFXdSxX1udzc5uM1A84POm5nS9t+RQMN+9DBIpmm8lHboxReS1gB5TIJtzeFDpvcUNGiqWNLTT3w3qSIfdVLRUkEa6mi1h3AS4ixK/bogZGHwRg6Pw6fNpDihmrpMbxTCOe43Gu1QbzWLrGHmGcoS02SiMaGKcLS4J/nMiYR788/CDusZ6EPIg0eKDGsA59C9bh+McL0AssczsZ+IeuYG1Myq9RuSufzUL+79fcv68pyG0WRphznz+UsrQzQvtPeXMWlCgHDcXUsU57VTuBnEVJuYT7XuS9X7JToCOx8OJRbfoTPQs+ovW/XnK/jbRt8uzTtZwLaJ/2ZhjGG6Ipus2dVgAVr1Fuhpfhvk2LWEJC1MDv7qrDSiaWkOCFnrnB0rxEuRt0hi8i+7nEfHGjX8adPhWPDYxb/Y2bbyMdpGYoDlgwCHmJhlO3y13/N3Yxj32AsSBGEctOI/hkWCxYi5e/QhmRepua5h3FxtlFN2Orfb91lCcotDVN3ER7qfEw8wc29O8v6qG9gZolkfqtKmXQ7fCiZSKxo87SrdoX2jGQAwWPtVQ/8hr6LZoPZrVY1g+C46TlLHGfwZgJxXnlXK92gXlgiZmlc4h9zlwoIbVrPTJANIIvLGZO+PDD0rjBzPIGy28OMQpwiss03xE5e6D4wdvfk7Mnq3lokoZRQw4W3kVKbrES5toxVQKlf/o7XVWQ6C+dfG0NyvDuA+NMo77inmoRuYTJzPBiDqMZU8DrnOLMSbVDKn1clkaplPS7TngrGbGUxAzpVj2cBgzry9L4yqBdS/0hpwrr5iMYvmIQulEOU+Yr6YFm5wQPstOGkGGAYBXWzc+fUTdsWbbEG8bdKMo9NHH+jBnYdVLHjH6Ve7iiuHucgb1CfMhJApUUakRAXbjNzKGrpdxIPsfnaxPr8U/x1Q3jsTo3BRcDfxb/ZnA1etlQY6yVqAc4a20w8FmbcNZSvvyN+e7iXI86A8/E/D6SPBk7CqnutF6KiIbdtgiWIsbQ20mntSOFe1kc6jhXL1TrGmdtknVSw5fpvjyi5ArzH1vBrrMP3g8oNA+B35QnSdnBVVTnhb873DTMz8/bwv/ble88v6xrS90ot6Iv5qcaY37RKvZ+HXKpnAI6TmZeGe5X3en+ifKarmV/QKLjlJTni+A5gD0RDxBS8NbZx6OtFxjrqGgPo9Y+R+sXqbRHU6lgGswqDmIT2Rqjh8yN5DoKpcZZW8DPsEJJLoBnYBQYpZ2oRxhYVvi+cgmcgobMWTtku9MLc7ctPoGHBfE8TGHr7Z3ygtCmldF1U1nV3oCCCXiRhL4q8cE+EO80FTNBtLd8N+fzeHi8GNwD94ZU8QKzL2OKgrM79aCOdumX5ZAV/YLI/t1dVIzmtqkiWjSJpzzZ6G09e5SYI2DMdj1AjJJzFsG+rth08+17A1yUKfwnMoq121c6tVNgVD7jmtRpbvvXDbx8Cr5ZnCLLNV05MqjMkUqGyVj7XUcQITXhgQzNbghNSz+axufUWJjV60g3gAtbH1t7PICiQpvmFugG72LBwgCPi/0Eg7v+NmZUz9WFH+Ls93Ijyj3WJ2VBDmqGuz+aAwZRxMaiJYhWyg1nT8XTD4w3HLfUn7CsfJA5ifOcrJp5PhftoQVLVBbqkBNyeuTrfg2h27YQqPK8ujxjW5J7LHHVkytDN2iz4QnXGk44oNKBtUw/F31bw9IBIt4rBTVWLwdPqOBY0B0DP60gqa6lgN4oXuSr1IGfpRdnOnajYpjy8SK8sTRaSFA1zMRnkhQkDhZpv7twiog3K8X8u5efIkb0b6qL3x5GfEcBqQErGn8rSDKGrkBgdq20VUh87bmY74xGI5eSy3h6LGPIMrnDJX+oS5f1MyACgd5oIDYF3TQhh9sEOmgSxkJ4zRPAmDqaT0nqN2J8y6FOASct7+tNT3isXN2IVlnqNNffFxjr9xxjlMKWRAmIWVnQqs7aLQtj6wVI7wUWMM4ekPCiVXpDm9ZW1XW4Vz0K+sSD3/frV4zZ6X7m1TldoSR1vl9C/Xbk3NWVdAX73D5vjXNIchlF2phCRhibzM95EhZfa4z7XqREbyyxdG3CmGHgq77fuYy041A+MQIlyxLjidZMy29ja5gOBuFEh6JONBQI5y2hFyzI5y9hWq4Q4/cJRvrn+3oa1MFsQ8XeNjNpoR9VBL3uq8oe81fkTsIwhPH0pBZocaY+xWTA2mEaqgTKZpuMsXA70BhUH5mzdXR3CmXpdrifD3vMcbbqs0LJYQtx45g7sxRtS+aHFfHdqwQMIS46UIourBNSg1rwv89YVlSmwteov2+V76EAbl2Zy5yNGsLFpPQ/37k/DIwFe4tkOomcOmMErliUT01SxR5zmL56nYuY1nuebeXj+4KXbvLgLKrwVl90dycIUXdApELVSDmPGG8Xuih2iWz0UBksXFJPtgDbSr4jYs2jZB3NNNY3IEwDS5ivHMmvQY8nrU3//ezYnUo5boh0vZjH/JxTrjvWc20+a1L2+DwYJOCTwzPviZXdnO04/gcDi/B+Wk3xEyNbjBcpuUM08jRnFXunJexEJgpvMbylGEGcac+HmNqzxPsJlv1pEhDhcrPoCXj52fxj66gIQtDCjclkwa2545bYEbV2mHOCVx+oZ+QJbR/3dPpicaDULaXvAmvxKv5nHUl7quSg7WzzfarDA1YV4FXlyAHNXqQoS5z1BYRW9jdxuMTEuiGEmGQ0M2mlz3qSqR2TSwOomdxFYXvckyGcYi4yi/V+DOuhFBT4M/d5bPIlqW6EshvFF8j79GeslWdo8Ggmhjh7HNBzh5GosIMcG5TlO/dBswlnvIikLYTyIWEU4k3W7O4f+0FidoVqfh/xbxXmw6DiPFclbad51w+VH8bHbjx98Du9IP+yCpI6pLyoy5jqqYhfmBOY6Qw5lN91MkFdNUFU5xzEbm2Eci/0Ft5j0yi8JbmjTUWxOeq8rQ59gN7U4r8jfxVm74vgv0vgG4GJ6TQvFOqFQlp6+6uaY0Rl7Osm3h9yV+qZbnNl9TEgX0M/uQbZ5iL67fdri3UiGwUurT2Pz4rmfvk6PSvw5GAAUv53LKkdiwriy+wFvWfI/p2P1IKbl0uayJeQfX088wpzUXhbgdYEc60eE5Xem3YMPs86DOZcRejrf3+f0VO1wTQZy+wTcxv3FNIs8xbNvh84duGGJciw+z92A3wi+rUJQVrr0qjOubXdkkhqdDeWFuHcAvvPt6Ropirp9KMK/B2zZWldidXPSvz7McQ9vTsivjv+/I8Yu64oR4jNdMJXPg7z/7mu3w/WOmeZDf993GF5KRYPHmIXhEj/p0O0uMpGeqqX+VR/P7k1I9Hfjy4uZrvI33w+6P/Dd3Th/P1roS3GMx7Ed3/P/j7gy+X/3Tn9r+8IuctQVF/NVgP4MeXFAyW7DanhaGKF7P/lPNKDZYrm4fV33vKfDl0H/Y/GpPw9VP+zO3j616Xb3KVzdfj2xyPxz3NKqCWit5K/PSW2F0m4WwPlqcM/z6PPXQJbHMkMZWcrAvwUH9ldLHQCBhJh9nGquyMmotsE3mWdbedqFL24oDZ7/Nzh38f3t6HyuxBUwhFzY7Yb9vL2mEQGllWiu4mfoviNSc0n3yj/5q2GGcxb+817jmnKffDZKySaun5IDIlc3dpzD07nvoM9YvQi83ISh9z+0ZGzI/uZtvcdAXz6LPvmOQbyzeTD/D8cboR5JfSs7t3NppVO0GG2qFdZtziC5rXIt5FPEi+FfPdbo8lmO3yemv3B4/5Pno3Xw/BtZTfLCvOzNh0wxcmX/3BRmURX2je13Q0QZZAV6f8me3Iie2gEIVbhgj95RlxEmi7jrz32GBOSTUN2U87U1lW+H5+AqrGO/MtBP/8whztJx3iBeknJQsq52nGumrbSr1ENOTurl3Y8yqJw6MiKn8r4w3ubCsOjK3hbpKK/UcsO8mYbBxZ7qylpzcrsz8YLZKKoncV45sCzDmnhwygx2QqQf++NpR9XBIdieWWVFMu17/JSs/qHNPJ6FTT3+l8y/83g26ALD3GVFhax7Ir/YM3miPIHCRrCY8vCmkatJ7e5s8vqjDmmJSogNnWs9SzNH2qZK5E7xSFmWzw3J8XY1gDf0QuGxdJ2toyO7zDMU5n94vNw05l7tCJdH8ZpH4rfrxgBSMKDxGygZ/0bP8ByXxPIjXoU9XOtPIk8bGd5qKhTWw/ZzjSmgODLbtE/4qtcB/8bvsyOTtkQS8XF2OTncvuCik/mHD/jew+CK1usdih3nUaVwKxuNPpK8F15Rs+vBczKzbA89eSPb48U0Sg2zSh7agPGl0XG4b8zSknk4kW6/KMFLKSbm2Ocp61mzyN4fcxZ0wJ+uvjPd2IWCnlywdJtdoj4RnVK+UbwtbBmAtbSMcF31rlpRq8qF5bGbV5ne+Mt+G7T10Kk2oq03Yhhnok/3s3Nmtz5gTFxZvQypr7sGi/RxqA1MMqfXt8vMY+qr0TDaQRIoM6ZGCuRM7rJ0Wyz0IxB9LuyW63kTYW/t9Jlz7yiDjTHXpZ/unauCPpoq1j2a6cAg07x+EP0v447i6cod6SEFecuxYh4l/YkttSIvNlaoxkLM/TDTECW6JybfC8f1Jug77a/Bguz7iyDhr7TZdmDP6gTRi56yMnyEWhEnqXSJcIcyH3M2ExcMUYtSEoy265oF8jSPh/RzXoCzXiSl2sQmaILEUU0kg66n3jwoQLvddFvI73QuyoUhQ+5//gzs1WACzEbPIAPVWJEdiGnkkR1F5j0n8WywPr0dExOzR75EnPHOo78KAga0388QgI7U12UEPdpjogH3xwSXBrhgfLihPXE1mwMNyJFnq7yYhspWZAdZfmnvxoqRYml3l4Z7p63t1V9w+vqlfNCbiR5TyKVEUf5hjDaiV8WCwYDUzShz4anV3lH56NMe3gH1tsrV05HymwJwZSU05MtBhIiaZpgs2Leh2NyFW+C8vrZQGEm1MtK7EVqMKQdXp43CmnvcmdqAYxnq45xkpnqsjuKhgSHInlCES7m0z/Q0xe45c30b4p7xWeoF2oxe3ij/8N2NWGaK9/35C4wUFEk98MjzaCVwaRIKO7qca8gBIrDYqwU/ZF/LtcWiQf5WS2t4TmMqQU1KrTqfVuVgZyezdIOozmldLab+hPfLo9CYLJrvb9CLbns9izr650onrqo/CyJF0NEAdNnqLYulSAGP9sR9TBjXfTa9Pdygzm9JHjv9qB2A8mEffzWVcKOP0Y7Wq6gSu9ioxrN/o/jsCr1EpeGfTPAqoulsScuKt2QH7QOvj7OXc6eWwFrB5aRgjwE66dxPAnW+AC5b4jxPJ+LNJh+4XYD2gvEZ3VznJ/17GfLdnea+9aHG5ahl11l5hpxZKColWebLZhW8VonqDs5yTam2uttX4+hev2qLqxPFsKfFdg9ruSTyKwgSPKpsXjr7zXUFMCbOFOtGKrfT60F6jdsJDsGq6y+e+Pn1BvN6f6zp34aE6UGvvWydP6Lu0l1iuXkuz3Twdrjj8G0K7eRvIHGrBsrjPNnSrHn5bdK6wiMZk8s9ufmRkpfHpLG8zZ8vj7SSMFxjurDGuR4qvxnydtObSAZLd1so7dsicX3l+ir9ox+3WiN/24QD7r4PoeyIN4JQpTKdL5ja+CedzPEVGV+bHdR6DfLuAousXiav++E6xI/Zn2a26b2UW6+mYWuxsN7O0T0RLP5RMGu/5DvL6AxV/i623ES+nn2zDx7rMGfqW9K1PjZ3+oZQP+pvMf07wgmlV/97EV89Xj0EgyTvrDLisVyH31yiAF45uJshcQ6W2lEUKWuXGJuuouDzHwqa1J/yNlYs+XwvMP3eynH28jJRjzbl1f3+WtzwRTOErE7znLuiQxqEa1Ko6wawPzsJ3MW2k2+Yi7dVDSO6s//NgA/+L5uGOIpEpIg6pmvt9XG0bePie8hDREjYZQ/l4/H7cZYL0tBkRx1fpAs2pf4tgLhkycNH9wmLDXivaQOEBnOnDFPEkWaDyOM+88K/JAYDFqPin08CyMb0gcflcddoHJXMBBLrPAsr0+IehZytqfyYajB0u/UAdRKWPsvdNiosMOJk9EF2Ond9DEZv5vNHgN3VS9oFmL0VUwxPzXNqzMuIJdzTaafFAoDsvBKvm/6WPoMcdxAGfgHv7BXO2qN4CIK41QD8yI0JuxWFhJRMrtmUbn4GW8i3jxfmHUAZmvGkLP+OTSF3EtPb018ciZa7g5qQ6LQtJxyRH2y8M1UpxdmCtqrg3p0hsS9+0Q4zwUKu1mjvC+qSDml+skIir7nd1HdC+jKLarEFan9WR8pddgSxii7nHHhRhPH35HFSPMu9Fq5h3yH5Me37fRBdgtvkI+1HWmxfJWy5AwS1Oey//tN3VEOty6v7pmYYCoPrP3puvVedprPMQnMX+QNe5DxYB5UBr2sc9KCo7PbMWVsyQCC0iBhSshvs6wauLblhxp1WOIv8lXn3+1bSO+NPMxn9Gj80O71+g09Y9QNK9h5soacopGmcuAhfvu0VCLlnOpbvE9Ch6m7VbrOx6gkpLdXpRqlkH8xx7OR2fm5hbbvUh13N6rzSII7a3p3Nj1zd3uXYdCRSOcsV1OVZILPexPN8U66s9vdRLtKOoQlsvW9q0Vwb0kgGB5nxjZDt5IEfZLh3Ffi9qALZXodcxJM8vM6MttjnBsyA3VvjdxGJ2P3LqFMFxlH1Gct0q6hrvzKDtNJ9IXc5SBf/D7u8kplXsemeLQk4m1o51rsdnh8vW9O9rJliGmS8VkL7bTsxaD2ydx6K6f8UR+vL1a0RxjHKdpldNDlMU7KvzOmeqIN3mN9Ml5bo4tWUEdqeQzx/NbSbpdhuE8IV6AS4e5Mr2rNrsM7Kjj8PA/Pe/F7sz7uWTk3gta/3qBGlzyiORII0mzd+AqJ//prTEsbWShgB723Zxv50FLulghPWw/qk73Mbmgv8pBnHR3MpyB6I6NULdze43x7a10kGW1aE+SBdyqSW4ze9vol9QP3uetQPKGghKnPGmvT2l+Ev17iWR846KTwGaqO8aRM8qgFYXRnCoAZi3EOPs9gVHyRg64HwuJxMVtN96Rgx+yznEQr/s3mvrN6mb9z60QzdsvL+JSQNereOOR3vSc+eqOl9o1EeXOY7w11c152I4fZ8nDeny4TcIo85QkaVTHfINhJfQzjQlI3yj60sbTF+tge30bx0cYqiR+bdTrxKnNO4a0+r+mE4DlPw/xsBdkajC2qQnunbtlymGMOL98hKN8CxARJ97e3wZrNDqKC7J6ctjnHZUU6fxXvxj6AiiLtE1MU8PvQ0h9o9Mr9nhR/Nf6i2Wz+bq+tR3pedyP+03p5ItUHdUxzZjjPNKLejFqE3ClNDVu+MTN4+rC9bvJevVbJNFOv1NgJ/bIBTlEubQd3fL/OoHUcpp3HlU816F7dK3lVxNXhODQuRxKECJOusa2i/KacLqJ+Qh774pNMPLFBPiggxEB7rsXZn+Y+xnBhqdvfPot8JuyvLdXuBbscabzaWVyUiRtdqsdhS6NPXptHmGHNTA+65ef8hRrIqijOdsb6+MVkcWsr1aoVDLjt9eXpIZ559VnBXrA0xzO9zdwp+jR47tEnO2RQ2Aivl02cviPWJCOf7RFCw3EfsWlRtPIFuyouQ50Ub5B46X+eQwZJiho5HYN2GzpMifXQeEDQJqORzP75hBnxWUKaO9ZD8Hyb4wRdkINCs/2Fd30lpM8I7ckgFfWrwdDvW+nJdF7hW5RiZccWtOkimKfj6db+SWO1nG7Ow9U3xP+LwjgQdsRoJAx+cmj2Zt+lWAU6miuwUkCKZJyI7+t2pEKSRimj0k0vqDjcq8c6w9elh6Qs76xZ5UTa1IQDLYFMzVECZtr9o9upYhHlpL5Cmgi5oB4bpYz6pOZtbktuFtVf+4aJ5citr6Ta0cpHiTxBXrnDvW3AMkJJordesz2VMVxFkCf9AU2oaxQUUVuzV2OqDnn+YCF3+U37Nc8WfLGFui9w/QJRDE/ly5xOHX7ibvuYnQf1vvjlCTnUkmRtFqUuGpfdd7+Ne6yrH9u3850M/Wzaouxh2cisfNKHByBbPK9hgwm/fHSVKhRW223WaSZg5EkrrzBjALHnhUIrEk9zPRMAbS6mveJcW80QC6opjqecL2T9eP1PBLvGg0CVtXS3dFn8XV1lI+n6vI01i8VPscuJ1gER3XN232EKdhk06yj9vKw36jL9THaKpb3Y1swh9De7RNcptvGE9S87YoqhEVEz8ln/xDhkjznhI4w3caJbXf16921PciNgRfM4Hh6njYex6f3oY1y7eiejjbnHqgf3cHcXsdg1tDS+k/1yAz5ZXycv29tlGyyOxE+1vmwekP4wfV8ZKZ6aMCq5FbO9nhNXhzBgy8Q03B1ElrqolK5fyJLgMdMxurp4MRXNlP22NAYlN/nXC1rRCI/8Vq8e7NWLOBNJn8OywzC+sKua2CLhNGYchqSrDWex6/bnQ5ujXtx+rAZqbMiJYXTjP5ztX298tGazLZ+GUfffcb9LoXn1J8Qy1YI6Xl/5O36FwV5Q+tLaWacvAtiDToboTg+jDGWZSiS4NIwwQixLijGA8tkfRb5fydsyNEyBmgw83U3xe/stk/iXTdNn/gey4que9sOjnFrK7d0kQTZR8IGTtHOxHy789TaZ6LEgGW0MjVTHS6rdYFuWm9p6X8dtw0OeRw0Z/4c6xCglV6CWo00zY+6/zBjZfcMh5PuwFMNULW0xE+u3yXjfpYz1RdA04NucUlB7pTSSwjx13rLnMj6fPt+bj96Kk364s3GtMrEsggN08iH800XDbCNiKmcfnYWz6RtWaTvLKlTxrJ20OO+XzNOPuPHAkajwizRnJBpsofsat4lQ1/hmL9RQwzVIm3e7kcRgTW+wDTxcoxqnlC92qbdYLhp04Dbm5bOC/KZpu99Yjz7VF3emvHMr0q3s5zHT8teXDeXv9Gw2awxPe8H7BI1gfZyz6icbaa0akFuQi9kv88aAM4o08yF4k7Ka8R2jl13S+8Z1fb3s7D0v0Z2ae/5Knp1mpNqXsk6wNMGKDNT7x1L8kGvLMQe5yfwiabVibIrnPgvi8Nud1m298VJfyi/L5D5iAcUIXHfQbbJa9y+TcaMInkQw7yY+Xz9HzLTwGQ15Hn7rtqP1t/nbXB1kghKjJekIkpAiq4tbHuYWhxFncoFdqp5dbdYspXfpb177ZeosWcg/HPAV+qJDel5djEf9xgIrIpEgSLzMscyoWqOd/N46KlbxDRjK1TX+AFmO431HqPXQ6nPW1HlSAqCpUcjDxDVt3VUCluAEqf9ZYr9OIKJv3iWLAmNEU7zJF8w1m8KkZbK+yZwzWWzGS2t9MEluLVUw8uTZ58xZq2cuGf6kSY/RPQhp5R5idmnrphysbYdm28V0W11JOWD2GkyuKmAVW1/cmPJdEpz/nmOmkNuOVY8uQ4BnboYwc4OejpCf5tPZo89BvyjGHd+D4HWEk8fPyQ1dodhb8wrfjpOhR7GIVp5eBz7qt8nRT5knAphfqzQz9f3LDV29c1MEmLGCqN/QW+7cF8bbvhz63IjXBl9ctuUsefanEFM2/24Hw5Z6yMtvt7lKM7qfFBtawv5dWiIUq1lelt0qxTCah0pt16peLydVvLmXJKTVMDezntWL+7Oh1SEiDdM+593Czg3mhekI4lAjy3gG7z41WYFkPgoq5SDmcMb68zrMNrtczVg7SWR8WYm06RImMKTpQKkJrZjjxdOgKM6Lyp/X510czsanihC7rYy2ap2wVxPvej3tNo+uj5quX3DTYhcpxzlvFZUdXR8KYH3FsKj3aYr1CaT06maHpcQRTvZ1oid70n/1oD6LRxcIwqNY9WN0eI2fErkTOuYRe2EwTYVxR7j1XB/cnmuTJcv0oEPxmDgXrmm4zlKuys3cXeBdOQan3De+9pDR2I1T0zDc1dtQjhw0WMthv2UVWH3y5la7i2ZcEsnBPCWN1maKdUHnYinA92zCW1UxmjvBk8RDfDhYD/q8G1doRbXSDlu3LcegEXsp5+GABFkjeA1ChWg1ROUTLLsFvnb73rqfjfT1fbC9koA55rLbnxqr1D6vXnkxEDOPOce+mxHDp+0rfE9YzAf5rnqqvfCKz4fuyOpc4mfmZm3HSyrzTvKMkj5HsynqS2CKu+K0w4dAmsy6SOR9qCU8XngTz985HDkVVrSfRofUP3KOMalN5nEN2n/X31dtgj7x+E5nIm8N+7OqDnuBPzWvV2aVR5Jte5ADiiCWkDC3GpM4bLX0C7Xj33nCYw1RJBqilQ35M3A4lB3J6RtzqX4HfxROvyq2T3z6mNNtvnnUI9DaigNeZn1R38JqhyWqpVIPdz3b4FYmnmK3iR4ipngAHq/xyu/RwIEEgPtP9IgT2PmgAoeZqCd7bGjHhekUDRDUea18mjUcft/8/XQ+YgZ3R7wRVdd70ktX7/SyuhQiZa+coH4h4+vV33ha30+LfxT/zvAxPdcbt9nqQvmdWMUDZSTBVAjVRKPcpTUPt0xBK864UBsQ39syMPvsBDSoGJdaNNfTYVWOU3jPbGE9gYcEJeaMOs51unxdwCkftsL+Ij5PtWutJZm/9etTBl11dVK/Go9SV8SifwYbTNeWCsiSx7AxH20tRMIKEBEzVu3KTCNaIzWvFOLBl5LXeR9Vp7PBY2D2PjxAXcmEWpTXY+su7njHjj7exxVYn9z5dhx+t6NZnjzextzGXG72mKPxl3e6hw4L7nfl2tXyLgq3FTfnrjvnjByjiQ5YM+ajh/kU1GnnswaLudYjzgl0zjdEF2FM1uZfmEZZG+IqZPSlN0TAuje+MnxH+pmEIMUPQO/nEvlpdfYPo1Me/RZ91uG1H0htF2udzzWDeEBVqd+niYLn0ocqbyxvWDXnsP6xTFRI+1wtI2XOgF/kc4bv96RuQu1yxkj23AqfA8b8UHRqh7p7wzO7tHtd+sOmFKCQINT7AR0rQqBzSJv+45GQK95OXxdpd0CP08vEO4PvyOXVnzezFns+LDcSXwio3p4LWg31etHnngNL6A6wLEu7SlyYd7ThPuvcdRa7ULnck+v14uzigaJbgMWzJSI/L1Pv8N3cBC9Ue9/tYZ844l8o1VI+v3xQsP5FRwLPrCgdXV4XzFL2IY8B32nbf4iOunJ2urrSXyNpBxoe7EyXv+9yRySKtBllij4ur5rcxEUJaF+WjW/kBKk5NcUbo0204oVlXnVYlh7l40Jsr/XryVT5b+7ZjdabOVQv521v4txh2PBgbruPr7ZSCtrQAR+izpUXDC2Ur9bpR9oCZF3/Tnn3OX3sZivfrM0mPGTnrCgVziZlmrd2yW1zWTqsl8elvWOJC1TyfzE0vNKkn/5svGs7QYd13e2nvAM9cWrXuQo85929MBOqZwqpjxeaaWft8T9uIykMgRd934uDl2ZihwawC8JtwPMRboWMpRqimq2QDXSlPunf+VLlO5LPi0cNCL8h2ir3gPrAcBv8dtxhChWkf500PDH7g3LZfm2u6bqECjuJMlbqwsd7Lezh3f1jc2S4LabPfC1F+Ha/5ypc4hzFqAKmOLTv/BPaYn8tm/HzeJdag0XWNR065FWmFBTxdTrPFlaE/BWa61tCUQmXuxeyIjmK0uVd8FlaKJWwgo/XEibbOLcVFip60z1D9m2+VflWP+6u401AU+hvX+/MPTu782ShIxzU29hqSDDVTbE1jsACiSdemqM6ke5ERFO9n81eRWlg64zHct4iX+QRHyL/+vHiAQsQutLwt9Jh8FJbeKplJLoAfvaSKkviXstDUo1Jlse5bqbvGbLzxFqOmHmcJW10FIZ2EdLTVsVhjrQ8WTJf9m5+xBOYvPWyDhfDDfWfGyPfWlIl7VdFpJXGQDp6y1INg9NNYM3+umAfzrrqGJE15KMtvfDHSUVmf7bIJQ58HPwLZ8MoT4FtuN7qg7WwFXJ0Uc6ueklqQOoOzZOypbw6xFsQfcZeDIrlax1KoxnHrXHwlt8FOKYFjNmsFyfhL203yetdUwk7zqYpVd+PJ2ATkSY+hef6cjjdRKzR6CvuUW1Kd7th8Bq9eJfzDrPcReqBVmWTaXyximKMA/jHJaAyq/REn422Vn+Sji17cBZVzaDLUVcjO3XiYf+l8gTPfF7x/WR0E2txTR7YU7nf+JcgFB/3ENJJjg8W2JDHvDBusJdS2ZSPlm6uRc9XUIGbpql+x+sPt1+LDbuQ1zzJmbiXdHY9bQFVxAT1dXot8eqnefXWb/XGYde59uZ2W/TnIyv3FSAGbeLPV4yZvKFAr9MHX3+9JuUGVaeY9/1Zabvtw4Wdr7ByChj1XaZQJXut9z3US5TBIFh33rDVtL5E69RLhJF5kL7B0pGid30IoUOGIZ5NBLVImFJl/C4VxfLAqdrLP3kNU12Osc7pUNIY6i9THU2406wFCZnm6oKslYv8ex/1vGF9XEd/LgXV8R6P1+WbX/h/3UXRLue7+NzuNtKXnFouXbP03ClW9Xx1xlHKTyv3eoZMDa3IWPWs5FCpiwkjazKkhIz+05xexm7pktYju/yRy0lfqJn1WD024T5iPN07xyC89fNCX9QZKaz5XNEstd4toN+InS5MUt60XBTvocU73QgTdEzRNI+VD4VxYLuXa5DcYmTPmciJF+wRa50646l4Ni/tXp5uyF+veJvVtNlIZ9vT9kl/zS/XLUjmdl77I/xXxdZwMrTFEx1J2g/Wr86f7HZyd/cp5fcP7uLe9BbjiHDHnjLkwVqV2SZtCxXU9H5cjrbcyul6j9VoZvksgRb4zG3vD+Zit9Ytk4B4D1+MorqZ6s05WiKpLKODhtueqbwRC+b0kiuqyezPEPUBYQynCehHu5vLPZKu9fKVNJNKrejmzNvq9LDjN9OrO+XaKUsPXfIKbqwGtRCzVWoc2vO05KTVVQVrIPH9oWb28vxV+WXGK9YFLhOKdidpxEoQ+DnDu/ma2LOXc2cwyPGeEqyk07YxOQBBhxx0LLH+Vf0Jw/f73GzLd2ChO0D9pbF9wA24h667Sw6jq5mYkjXm6AWbBn0j9MVn2hmJ8G8bdmJZZlet1Qwdng/oWO7pK/58/0wsy+0f/cURvIrn1oqtboyKVLKe0U/zx29b6H7Hj+rK3j897sI9yuxy/TbXdcZn3epKYl3Ve8W5GrDYHfJPOVQijgdaz9csnbkS86pdAc9jfSR0/Ws8gMPtPgbfZIzYL1zELxhhommlQQb8OoHETCXIOdQ6y3IV1aDzkbxhu/6juOt+u6vDNf5m0OX1Y3EJeXLqlTxEStbCRfx+OI9fI/tacmdpYhhgXFCBRLSFuz1I3U0rP6Rw0D/fNcQH3nutDFCV591mVMtrXWy0cvwa8Ob2QvzWp+5xHYGhqneDy97edhovI+00pKxJh/ROKK7NF/Nn6omXhOVs2/GJZ123Lk6z00mV+0fAc0XpMuXz3msuf9q8C4p8Z5aq0yiAjWelXvvCR+Pr/BkwTEk63CIgO5bGmLvlcncQM3YHdfeFzTZc4zsHV1Ub6rmnyBUqfdttuQVP08ZUNcMGzDBYxA5n8KwKUpdgp+drOo0jUL/Zw6jgk4M4Bp/4AiTGHKgywrgft+RJNjCKtv06p51UvhSdYez/UtsVY5fLxWO1BMokJgjwmFgUM3OPhEYjc2iJ59LXnP95QLbLJjydPweCL3N11mAZqTu+qhxgXRyDNrNiRM04YknBL8mT6DNb9XvELXYnRc9eMtAz/tQQD6d47uWwmrNNkQ59pox3FUJPA+taVed0QZRIPAKiMW1IKzhm9Ndwlq0qH16bL3VGiR7He9o9GGj1thJ6j8S38bEPSCUNpxMgGkMjNGBL927u/hbxAiaNVVhD5pCKeUVqRIQXuUIzRzvnRfp6dfJ2LCBSypwCrONBkdV1TL8VzZ4tZBP/yH2MwArinJBIu1RdGPPf1ilk86Qr0piUwGM40ugemE9Mz32rzd2tLuC7IvIfbLd6et19ripBKOp1dIi0Erm1poYq3HKtKPjFXFdE06lmb6Njpe23BHOvxpQ5wh/78h+rV2937EyNb5Am19RIyzxbAk3lIfVdIyW/zogkKQnKsm3ojcLzi9JNE6LdLds5k8NJn06ScHGPqd4kknUyTofA1uhTvzyDY4t5vlKC0hvDDotYut+fC+KtT7zFXFEGSbvawwL2CVnz+Pyq08Hn6Je68XEnFpAXzZk4xVhL0tvagO9DEbnj9S++xMZQZe5CpvIBPu06FEcI0z29lZHyxZXYH379dYg275nT70uGFQ8xxd6qONHHRLSNbk5W2PjQsfopy+T8q7niRC8ki91BvaZCeodeR+bh9hVohtbpp5OuTHM8fS+RZdjZC+uLtvw8APWPc54TM9f2PRmaVRJsAR1p4E/fWf+X2t59jtDF3IvwauSTMNPbaph11xXWaK3SJoSHR9Bucwa1IM0csNeh1rWdJXIu5Js5WiKx5xgwVdTu5AacPkZbp7QDzq9qnNgnHFCbKJ0Np1xiXvQNaN7AftY+F8gj4+V4GnTmy2LOAuOh2iCkBgTUA+/bv3dE+s53/1vtsvx7p8fvOIL1eyf+vSP/3vkbp/69k8I4Dvp7J5t4EE3eLaB1couBeJOXZb3a4bNUxbfVvDAmi3+dpNulQvMNcrYCC5N2EukuI7Wf8zmETrTsS+ioEB6+yugckFcc90RDTEqMh3je5sv16inkEgpuN2Ln7m2R+XWrAl5+3Y2qgKREESlNqqIgzd2+EvAG5fqSSXuf0DEI+aAhoFMonsvIS6tF7QJHvxjKFaIFyPVGkjTXHMJXBkvp0DIK4HM22GboyOxMj6p138kMc4DILc+upLuISA8nhRrW8Fdk2i8NrEsVySIXNpjbv83aPI60uxVwaSoZEjmaffotiWdPEZ03+Gid37yD5dl1v2MuIE/YYW3qoi7oPeqsnGbRiKR/XDvu95TGDWOhc7d/8V6R+d1sHW42vk2Ws47ueOOhJ0d2o+eO+n3B0pupF6SDxfZulc/giXo5OIP0M3NiebQrhko/WzWw9Bvrrh/YZ5/g26l9fzuCP/zbE718grWelfHqVFRc6/qVFDK5KHdwVFlzV7fgtfroW5bc0k7PINzn9zW3+vsWRa0LsHDDB87GlXQa/u2uO0dTJbpFIia7eBwbmJugr8N9+g9ycH60JyZPE+IUqnpzZGr8N3sd722SN66K44+TidBb5E4s5G19UzGF+tbyjbWVjuAVeTzMGUE+iSaINyBgVRaf/R5LenGe3WqeXS4GBulOeGAjvOMJKEjRSrmGFCElc59LQDIdWFXLgsglWfDWR//kn1m6BjEyviONDfPwrjJ3crLwh/w0sjP1Stx9Ris9FDiCftxXVgKr9BP5AF9Jn8OOWrfQ+pcR6ID8pdYW/NuWN6TyrO1ZlWR0o6unL0hW3FVxImUPp1CuYZCVdTydZf5GqRQhqB7qU5umJ0jpeUXCY88xX7TQuRMas6RSJ2OdnSQC2tL6l+t6h34gh80Gc40A9vv2XWPQtKcVbzAg/j0Xkb4hk7HH0ipHSybUoIfqLqS55+LsMReRMYL1/25g+XeB79WfsVQX2iUkHTsR97NRceIAtefLy/jexpg/M/rh4ix3g0GoX+o1MFVRU5BWuMvTvt6nuuX4IkrXjc4924CuFoXW3CHcgjgzLE+5df5Se4H5Gm3ZgEyRadXbrx7SO2CLFvSYDrXszJWJg/c5sBrLprdAROcDIn4YpBSntt6uF8uV6in4JnmFeRuDxwvQU0FmpvzZR/JtNO4kX+Bux0tPaE9f9zMu6rHWZmJui19jHwvuRnrKCm2hz/QiH4EPXFh9EWKNwJCIPcNXWJNQnzqavyy4bA/RBOd0ULwURDwU2anU0qLJ08j4lH99fHegHAmLsiuHsUG+eOc3m6Z40yY0vZT8aP8E/TP3Kca3UIn81FgZRcO0s7N14sKePJfXLJZFQMfjFiPeIz7Zl9KXl/2Cyhh1PBUsZTBTAx00t+Udc3dbzb3Zph9Tes+V3441HETN7VJDqyshx4C12m+arfRtyK5g5DB3PV9ZNUYn1+gegh61w/QhdPJtPI1N8bAJT82VxTR3td48N9OJAeuctFgiU1y/Pzp/OyzeRMY6SJsjjtB7m7uiL/KsT7s1L+S5/ib4Q3zGwOQ0ltRbYtHd8J25yijCb0GPZy8SO4xB2TvVsq57anFBXnjZsOA7dg6bI4vWJfU68Eb5a6mt44MQeyfhDKNcYwLt5pbX8hwFiJ7m2Pks2D5iFZnj2Zz3lpltI0h3py5nWGKRVDHdJQWTlSl49kzKqW5zPBKWbdBv94FnImkvPBOoLb/CjHA3ai3dPVH+Zc+S7uGX1FyH+HxLHqzdEMcCfxLVac6+JZZMkK9YgmMlCjNuMF743Xgq3/R+oeTHNZ6Eu6gb/xICN+il5bV5l9LMDcQ/i7UtNdDv9QFdPkDGGXJ5KZt+uTthrEvJ5fSjm89skfeMrSXU0nFdXNJLnxBBrv4qPqvSgsQNRxedV/oVUpyxuvxzlr0xx0Zj5s5YGHWIJ9nmadh589tjwys6LLuClplWdJLlb10liVCCdflvx8eS5Uh5zVqmZx3pi/mshQY8hCXbV4DqcqRuwumCDr/nfr5bSWChFx+mh4e7k6VcV5jQy2T+Xc491v3ffFviJf8sp1V8fmEMet/yrkNq/NNqD7/kdq/Q4NzDIDUOr1LwdZ7DPBuLyf2HVoYRr7+aE1fZST+ba2OVUffyNSRVq6rrRbai/qf8MevYyNrc386XN9QLROIONxm9BedMbd8/fuUJn1++/H2LjoHB0BWh1TZcZ0JWyGT4Xud+OfCcvyNx1bWd5DLWuC6L57qbiO2KtgdkNHnO4RcvSbTXD6LPxrmLlMlENJIuF+bKrEgZ7+9zc+r8VyBgWO9xIaPOmfjfZO5v2GICLX3ELeJ9eAFb8pLe4O9cYRN0VTX2RQ7zL/qu8E7/3pXxu1ja+5Rye/2Pl7alLJe9i08K0d9rcX5GSJIAGfZYunAcE5rnOs12VuQUxRKzLMt+eMdctCf8HLf2JXxDyb1H1kma9fBcIHkBt7V+NWQpkQSbIWVxsNY42yR0BAdS6/4lHcpowEr6qbJssIsAHqR77zLznKWFrxBJAa9cJmlj+WGA1vyqBI/rhinpSnL+RwHaq8YUEJ2VrAC/LHgZwTNUzTvIzxmPgKGqA1juWeKDQdJnWV4YY6tQnoO892xZEHOWg/u87GStLoRW2kO/xMZq0SWk+rWv/VGKLZ/zZUvCzQUjkJU4+Jt9qZYYH/E3X1jpxdvQu9q8zYUV0CjNMW34Tu9IV2vH2DpCVRyKdXTQNn93R/irpQ0NO1unFa2LhxGhC+7zdp19h9XyvPJNtVou+GrFINNSH+loXUQ9dHthyE8PX97lp3pKRLd3Im6n1Gav+OoSRVHTPeWF6stfTLkGPhnhgji+87avqgjXXCBWUepurtcjHl6RZz6jqMJ0uibjstJKdbCFi2M+9qRBDXi2ZVoexdMfM/9+Ro6z4pY3rTylMvpgcHr6fu5LaqmBWplzJXj03I+GoBd6AQr5sP6yzOlG5UYaGJT7KRGqyiK3K/7+va6HtZeS08g93ZjrabzaTxleAuttpco38v3npNOODbfjJDFHItNkxP/KoVT793F7v+/x0F7/OaS+BRs/OOvVsPreySNm8esIi47OuzOcVGjMbsqDAaNr/LeDVm9qoiKi31fB1rx+b7oxKry7EeiivLb9zfURLR2yIOn620vOFOttwhZa7JP4XPNT+AjaYQYRcRFomUmJgPn1Z87V8ydItS952sFPAzQbAjxhsBjffVrpuulpdzzk3IzF+qS1ZwbvilmEJiKupozejGKu36QK3/1SKz1lvEqXgRT2onKnT8JZyQgK0L7ebZrkRZ5dDI3oK9KzmhFR7rAwr4ms2BRezlo+eA9mIVF7zcaYK0AkiuCstiu3G++jIRqX2n/G29U3GxLuMBAJjKX83Asb6ePrePqwa30fvD+imj1Gs9A/ycDwf8/uf8/KQ50xh5XnbsXQwUA24K/JAKQUtJFyvKtbZ65b1RSzNMKwvV6tAm5ZAG0q/qNprzSXPHfnzozIsOKPs4az0NITK3yi/Kzwxalz7sNMwbJo/s49VbW32q1P1koueZe33tA8TTg8I1JWDfzZt98pir2hb+RDP59EW1zonEyk71dayV1O2meTiOBNKLMz8QY1VxSQdwVdn6n9iSe3gsnZXdEM3+t3a6bmXV8yBVYIP587eyZZi3iOxm+OO9NpIGrFZYrR9Xo+1ceHHJ157Z4ex7W3Y1KRtlNf/mg0tvb19nIc7oXx/XJhwLr4/uuWtpcv3ue+oM+IM7ZAUtYOO02rSedKNXtF4CcVmjZ7L0LabfIqtxSTQoC6VUHM1QhBXOtgyw519FxcLnrnNNwhCzYkQWvZ0luDFsx81hqWU9eT/twWGO6Bl4Q/P7ntYdtfrN1xMjc27XwmrXSGblSOlNYpYZT7886y7fUUOhgyRg1XJPf3Xr+4yy3E8y0/LzWg609+fXyRfFwpcdUNrB7IF942hs+VWW3eDFhRDyuB1vEJCtrLDumSfNL3eJuw+r7KR7/o2dvnq9t7k19ESvE9a4NbDXeJztCQZ7zj68fj596Z59BPs9M55b5b784Zu7V17t6bl9s534uqyQfXTl/2qcVgkbyv33/1dsR3gCmVW0MQOlxV7qxlwflwerRnec+BidtIv8lt8fK6qFjf+CvQ1mf7Xl6s1XG31uh8TIP5d2EiTJnf/V32//0vfx15/35WP/78+3HK8K+WEKWK9P9/5//eO+AQywivqMxz060reRK94hz7GZz511oOytBd681eXNbLEXJFBDDk58Piqz1WLr5LIr9cy4FykA8qu2/7LgIbkYla0KqmJWlLSzq6Y+Ktt2kSZIEegHFkzdzXG6PwXN0VzkSIqNFC0uIraaFbaPcVD2Yg+QJQ/f6yPcEhT1UyrQRYWhXWh8sjIL21ul4R/m1HDfBvuyrBnB6kG4llb4/nwaeQo4P4tFO6QVbZQ4XT4kDrCAhRDaCEcxkoS4fa+1YwSJUec1qIj0XMoQsw1keh3I9lzmFejx5zZSS1fifbgrldg+/NE0NdQLrYPGDzanvXo+6yqJd3rQyW4LeR9GKukKljUftNNvp90rhTL10MBti+pF6/m3SYcoHV4BOHYlmzPEPsOte5i639Vg1d5AqmW8lrBqscsHPeO02HbSQY0ulatZ8U2DavEh9p6ZMzV8PieOqs2kvW6J0ZOh4utzbXyyUZrr7ITvuUZDZGv1E+4mK9q2TitMk/41EHjiU91pc6Ph8xuF1dNwt83F9SKM13XlRV0Kv3qmNK/csCr3XaOAAEbsG2b7j+DkrBslzKmonfXX2/EGzgW/O7CN5ll4EyqIPxsb/1TgA5wTnDNN0nrF8QEXdKj5PDbqEcD3L0aq6IkSzWx4d8Vd43CC3c3Jgbc1q1Jlid+EM/3be9OKrwOi/xHMmjnSVLad4dDAHehVbsNnHHFEkxFVIewbx95+A5X093GJHj9+qyFld8TeJ75nskhoA5qsAbLr3cBuQdY+05nqd7jJTx4Jm/EM+KYy2eA2GW5WYT+ksSo7z8nLeHaYf/952ewfIlkO+xi3VywGt5VAIG+d/QJlXmhf5EQ2vNReFDcAEIw7vVjuCNlsyrvkmDZXkCTVrZfpS3PVAha8xcoXyp9baEDj4gBA8mJhw83fEBBvQ6PiTgoTldd8n3KJ2HHkiBf7fkvmNSlZU3r+5W5Nap4psjHot7fCV1g8GvG+pz/YnFUlHeS6x8QtTUsRxyZyoukPMnCoJfkqzOhbY7MHW+D4gd3H5Ko9Ku5hrWOrEAysKL3D8Dn9TFX3sGR3YunON8X3kIHKvXZx22CwTC50qpIRRzxyQJ4/vqhj7gBqJMLIt2SHRbA71nmyQr46niW+wJ1IxRRnNHmjcZA7KE6CeavzE+5NEt2PvJGPUcYjGPIXM+GUNBr3mMioxhoz36/U5qOgm+WZL6Gjt5gcjv4vgby8GXU/CVeX2fuQd8jOjYQeAg8rsJxK7SurK7zl8U6+n8OQ8J5ntfZeuQEqr67uysAszFtc4gGpBZx3caPbwf9QB8y/3ydLhieLhkA69/UMO0EEfzbm99shMlu2MIjbjk77FEK54isye8JDSeiNrrmJrGdwhfwg3lwkRUJ7pQXC+iW18WyBcTrLQSPHIo50pvMkfunS0+6XG4gzp79b468EcVYzVSVEhKfljmXLh4EDHf9RSFuzrtHIK85I4HQQw4Zcgfilljx4+Qb6HH/K1nNX8L7OIqlCfNQ59+TdVnK6T3rMjXhJZ+TSj2s6aHH8FfM4uMrlIir8AqoTwTfuWlM7QkfObTPtWJMIrgye7zkvqIheb3zYOOv/GU5hkbf2PUZAz7b4xQn/1NRzrG+BvjM3NvqOYAYwyIjqHLS/pNubBgDAw8f7+f5Pmb5BiNIpnHGH9jfBCMIahwwIhUrc//CeC5CoaGdRvJ93pKqwTwEUtLxSoBxEU79XK9PEXnxR02knnQCYnc3I07j1ZYlj3OYO2CL+YUt8dorkPqwmUoUSvalCYMuAzES4CBAL5X0iCh3++E0wloTyhWWovUI5KfZHqapmFjaSk+8RTI4jezdpdbOl68ijZ5Tib3FFm6O2YhwW5hoEtp233SI9TFjSjqQu5i7Qo2xRQ6nxa7epYj/rZdXGaa16lmNN8iWVjNfAY5c3doDCBAso70Q+AsmvVsKyFzYhGdU25m9EB0USNj26VBXtHFw0/7igYF8w3xTvkJ5osD5VlI5G5AJgJMC5G7cZv9DStCWdqTngrcjWSuFExuAnLpqdO78Ss7ypCvnYvj/LV5V7DmrhMaYir6Nby0WfmEt4EZQPyVPlv8bkU4a0FJTmY378zsLkK9gVmvL/g/Lm+Lyc//oEIRtd3MgbE2L1CKEG28x/iR+P9p79q6FFWS9a/px9PLC94eMwEREBXQsvDlLC+IiIrlpZD69RORGWlZ1T2ze/bePefMWkU/tFKYkZlx/yLJDAnBHKuIeHLjKERc0oB+5IoGNA6+StHIOB2/7eAW+j6bufx47YABbVStmzWRTLK5qVO1cMeg6yHn06KXPU3GbWtjvY6ZWDtsbwjtCB0TY7RQcXip5rFr7+HHQIg47BTEYbZEDkdgIkUDOu9J6YJHNGA6Fu+kjtp7olEKGrypaISKRk/SGN1p2IpGiDQ0NlFiSFk/JLIoqSNsREiI75GkTnQHpg+cnJTsPCGr6TpZYubsBTor5mSfSPuQ8BzEwcoT0rYXR+4pBlrl2NxlNtm/xMkd6tMcPYaLgxBIhHyTgA3QAuzYLlEZPb6NE0n0ha2LODhCwPgM+Um3EgErX+YXzGPjdv0ZE1mJ+nHSBOvWMsXZwIN9vT4MnSGfmRAL2lKSRkxTSGun0+xiLznpH0g1oZRcO+gsTEC05GTzeEzYetu44QxtTVPqX8vUqKLGSxwseBdGePla4eEr5Ggv35D+aQdCEiJcyQZTatRs0sCmLfeXYUMuXMdz5steD9BsKmR0XQD3DFxcJ6H7HmH4/qiN2mRuyOdG7CalyzY2KAluOpV94wrD5EmMZtHMaZyGpfJ9y24n5ovyxTpr+oQgetEBNDm/0+gdFY0j0vAUDVP5YqCRI41C0QgVChPay8RMeaBoQHxMKKUNfj2wFQ27R57N5hWUHielmop39/fJGk2pYyoapaJRIo09yAXRWCsaI6QxzlNf0egoGm2k0UlDSUN/oAF8TPiSLI8+CQopExPW4I7HtpwwtYbKyZ2gZ7J9YUeSu65D3HX5MsnSZJpIn5X0cjKiiLfAtCsZesea4RHHzruJ6mOTPCv4N4iNbCVbYPUY7VpbMbNMr1CX9LrC+3Td5Qsl733IZuXKENaf4POFeh6T95UpYYT+9jDI0/Z8cLwuNm+s6rzUhlrNfoE8YzAhfR3ctjatP9m2zI153Ewg+e2nRjpIjXgvJYdvVS9G5hJiQzxjm6iTrWb9cgLDuUeNSa+iZibahSxsOhNpN2xu6GT+ag5weJvaZLPYiGwTM1Joqx8pDieGRlUBI0/Bg8cO2SCbbVRbTeecmK84/bItm+I50BgX+vXikF+7axb4v8umavZ3cb82mwb+IKM8F/wUrZgtuts4WOKi7v1p0TKH50H2tLLYfhJIOWLFBrfaF2towBrig8HbyR657fnivNU0oz2Y1uqLwekKRhUeNG53EmNCeooucG9mauQr2R2BHpsVX59lREn3TbLzzM38wOynSmKPJq0gE1mZ5VtM0ciIhi4ymbFZMH2tr+alWP18RxuTAQTT+26GYWRQrczF7to9yvmtfdNjLmsVSRpNKe5lvFQlGtuI9XCYzhjvJ7epXN0VDtgIz8OtV3eldNrHvcxsC5Cy52tgBE55hQT4lGv6alT3zlj/sCs3u836LWf+1tMKxHyT0TiiHTz3LvQ5RjB23+0NMqa3htM3gWS6YycQWSs3B9P6IfGryehtZhbX4brfmG1YOWb1Y/v2lL/OKhuRTYRR6cnzEvbFAoKkkTHrFtsCcud+TfPE2R2sIjXUCFdTXMO9DIKu3WFhyGZvIrCeX3mO6wGSyaS+eAtH6bjhgf2vnM7Nw3HZ9stn2xxU3Pj1pf18LrmkK+eM29PBoIYnwJYg3HE6tY/D8cg4nzEr9TSR4S9i8ZLHoJ/NEc6w9cp67OvJoNnwjq/pldmLqMeeA2Gl2NWiaa+exqsQYpwIc8aWvcmOuG+eW74ZHDibL+Q+fnavK7iYeMHlsr06VqGD30iyY7vdK28cd4ZHxKE679fm2U3Il/cqd0EfZq1B4bLJIimx3Bs22OaVeXJ3zvVZrrpPlrNz1Hhr79mYNa3ScZrrEfP2JUSP26zd5i/e03mOa+3uiBTf5bWr1q4eX8ExpGtwacdR4eGrPo7nIo7yHFb3u7ZY6eQP6Nwdvi+73Ex1ePqGx0Q2dphLOLiyr7WIxE4gNsU/iQnG7DB5NvtlxD1jxx1zGHfaON9upuErS8O4EDsES2tl9BF7P6X91E27YP1mzISAq3N4HfUxbjgOw3p12q/VpTRRFmbYpT7HHZ0GlYw5dpoOxz7uGlERRtxdZ3NzGZ9u8dtc1ABHNoF4G09iST1I2sFzaRtcJt4dtC1QyaQ77sxb0wgYbeCYR2Rn99PDvJUmaf/sPdV8/QiaXVYN5IiBAbl3XKa4Asxhmw8IaISLOccQ1c3B216wc9cZRp1P/fpsMY16wVlQWd+pYDXVT9fbiZMMoTsCmj01ljDusV1MqmbPuL6K/ZcpTyi72/Hqugzai4HDrICb3cOztah4DAKe/l5Lo1Nj/qY3B+hdbVN67MQuqov1smmnWqq9VI/wOwN+V6stCvG7xWuzMz2/Dbci1eVriVAZHpI8g+70h+f2cw4uOuU9W6JXOvkDCFMjs3TyxLu232KeYWCwpMDApSwqv+lzayTMz6FSGLZ51cv+27A+xUyiW64CL3AQqfFsUQkvKWp3zbfxag8jjS440r7pHGrDxVn0+O3UMG89F4NdUaH2DcKf2PM84McD8NodML10oo3XqhUdAxMcD8/Svo1f9FcjQkyUnckP6pdppf5knU9tLAXNbjmbQrA9PlzjfuYxfaa39tpGe62evLe8Is5PHItcGk81erp03lpTFq0vkFg1zEo4jOsaLhB1JrYzOkdacpgJ3K5LeNck6nurtuB5NEiGBQQtyw6i9ktwgVGwqFQM27qso3pl0MmfZE3CbMtTRnjveeZ7kyAwZq1b1kBFFG86DnWPe9tFtV1vxJYm1hp1CKl7Mrdn9sQm/txHWy7cmT+YklWYjNcFmzQWq4iXxnNwNT0IgSlOoOK8ZYN76y+BSbPAgd4eqw1mT8bCX45YnSJuRHK1JQzS85tRAUGajEKSYdumPauGU0gXrMGkly21SmIRZtBLJ3SONXxasnTXXZlDmPSSIgw8LSCkXZyOEIXzbZc3esO+DXm4XO+sebT6DCzPlYUbazmxMq94SSwmY8cV1Rkja5WaGk9nvGMOjx5LKYaMLIprR9F+BCSCw4wfDRBGTrEkIyxvyJNoEzIIl9aBcXxjKqZ0WV1mppB5VjTumrMSZ5u/QBBE92XOoCermv2UWI7Xy/zmpNBdGffyxCNshqeDmIVbq2niGML7GDhVu6MeT7s5Tw0QteH2YQxGQEhWtF3gGK4dHIN+H4MZybzN4pVoE7NoUywDIx/fx2CxkrLrvV2HMXiLIY4BxkZjYHuH3qVfIg4HyY9VGTcHGJfde5ml8u1EYBriJukaAmSE5yfxvR+OvZUqxxrRZs+iyw30eGf2RudqL+QtykFCR1qOZBoJPYsOTL492aDcJMQkIMEassRybH6R7fdtgcrICcV9dNOhlC/jjLkX5BV1olHeaWii99qeaLSIRkk0FMKT8w3lFrmkYS0lDf1OIxU0hqw1uHVvl93JG0t7wSpkaVxHVqqqSTc76SMbz3MW60+SgCQgTWOB5PqU+bzQnOb8RVB3U132xqCs0s0FwpubTH2X1SCXzxMx0lymMi5vEjr3IqpsOjgyQpFNettsgyAJAs+ChJH0KBau64gus8QnPDi26a0rLjEwPKyd3qSzKBuLBaJmYbIlH7TVXiEOQ/QR3L3sVmSu5G8HaYAoY8IoQ7X4yKf+CTTRZrHql09zNaS2FK6T6yuFcjuiLb6UVQjTGBFo5nsV0Y+RRCcM5hPO5nEJi0mNBMuucGpLFxiyq3BqPVA4taCRA1X6PqNs3gJ3CK2jF7SlRhHin05L8HxDO7YgOhOVatmHm+pD39q3ZvNbfWM0bltfSLPEiRXWBuNFjbDZWc4FCDqtuwntAiXrjtmHjuKRLTD7UOFJnNaZCfAbRS1SNLw7DdQIG7e7JBoV2WYpaHAEN+V3RcOQNJo25Z7GnYYnaIyilGhQ3KAnpi3G8WxORNWHRbRuasI3AgNPCvquJwTJBS6ClyYnWdPRJBKgLeQ53xEN904jBxouyA6h0ltClVxRLUFllLwOE8nrvOsir+/je3GoiqLniOiwO3KjO7paoy9QaIXcYLWHMNEF5HIsEp0V33khaSRsKRDtQCHa+lLhfgecq8QmRE3MDb0TUUJqDXKlMLCVQsd7USow41d/Y1+0PvVhpfrw6mMfWLyOUrvSeUpXe1o37VJFUAi9CYmCqRZwc4V/9hFB83DXQfl9QSuJfBcRNIiBiT8+rlmmX4PCYWs8UiB2pEBsFwl0sWAnn1RY4da0fd3nT1IKMFSUNT7mShC74lMDw1TRyJDGhPuKxuxOA0FcS9HgdxoOFkh8PlM0SoqMIIwPBA1CVP2eq1DgEGlkiobDzup9pQGOw1SoDMvGhG/NBA19KzUa15wQjS7S6IOVUDTmisYUaVT0s6JxudPAcXAsk4tx3Gl0BQ3WUuO43ccxETQyxZGepWgESKPNCOPqqnFwOQ6HojhmUGwK0SIkFGwH2Rg9Z4TdVjgSL0Cv9Wxn+k++RME5rkrQu6rGUqXaLOtOkG93dOS+yq3LqsjKPdVs2WCiUBMT8eWWwtsMmh/GFpDmdLb5tr5vdp4dmIJEId+jpUK8rlhTznQqmnjsjp5tsdKBa5QJx/bUOjiv7QfMLpha983OBLLwOOTMKcD4Ef2Wqpq0zMTXl4wpJCi+EXY5vDk4WQbNO2NLta5oKdrimmqrozS5Y/rYVp8sK4vVGrhhOhM4e0HzAFHmRn1AA8W5nDd2G6LlwVU6mFP3gtf+kz+AuPrZ54V3M8MnCEzMRh+CwPAFxnXdMuC155eeuT5sMB8dvUX1xap1emsdD3w/sNMnuSjIruC+k8IeTHHRhPAVN5PsXGHiqngeqRH1lMT0zMLXF/AQzXSo1r/qS1wJggCAPLHaoJqSZzhIQ/kpUUGQMJeo74ajTecVMrN6IxftR6yh0HMHbe1Roc4OZQ4QkU4QPddVJUJFqvgB/NWVTRSyrasqAKQ0Ju5NSvet+33x/EZxekK+NdHhlod2VZLWFBbbs8Bk6wt1v0uxGnwAqe4ntkKvvYka4xSr7jE6HlGH4QHFZCzAGvYyilSfGndcVVQTag4h5DUVe1lsDG4LXI+ioXBgeARodJWvGjzME/j1SPlP2xgohHyAMadr30FTT/HQKJCHVH3AJI78zpBtkfYkUWt+tR0ByiJij7itMMxYYZiaOdXDIcgJtdV/bytNsi3oDbXlaqlqywW+aib5JmYsA3oztzAhiFvqd37EnN6/YfqrDbHXQsnIkF2VRajbk8TEozsJt74qLe7xZ6TvKS02SlOtXt+IemlbyXx7p9Yi28sw8NyakpOeSSebeig7VmGQdWG2sghceMC+eWb03FH164AecHn2qVZbuHf8GWiY7lT111XVJWerD1qk/+tJpDWXN3EE+TRxqRYOIV1Kb5LDPdved+dq1o+q2mxxCGhTDP3Uc8QoPcJS6TgdUqTDqarMRY3XeL+v1h9AxsLCXhYRso4yJXsMfjerg1WhCkJFLTtHZOzqZOr+TlUWplg2T7U91WkS976iHKIcezwSL1S29RKxhoE42xE3dCW/A+7PD8P2vfpg7VT1YerkmywiLWH9iKYRElLQhoGqk1nsRPkvu6SuPbNM9fuakuDIvEJgQJEsHkRTube5T/XAo5iA+eSvmeV54POOau4R/2LqdFL8YkO/lAbdMyc8XdCyZ/pdg7SEqtusqyXpMDBVe9f31q62vc18pUF9BNDkx1Oq2w1baRAvGK2tYoMbM71spZOiaHyqtKzQQeueM1Pyk/G6MiuJcfaDnt2lUIbVVSUb/HDIwTorDRrBHFAfY1xswGktGL65Q1EwG4JgOd2Fih8KZr330exlT/cqNyib+gh++Dr2Vf1t6at9COBjoDe6av4dpdkeluq79/nfv8/XHubeU3FUYYZKwwtz0suqaoAGYbsMq0SJPwwmRBtiS0UbPgZOQ8WD4DKIB5AsB/z9vv1+PwiM5oQyQ5YoT8JZlvaLkyK9UTVYnXvmKHtRY5i+jwHUJW2npL/gJchqgtWCqP+5JMbarKusfGRaemm3Vb0+iVSfutuokVmZ6tNArZqw2WABYbOeyqYifUAyaUTl0OFHqkBigq5oG2FlGrBcqojFt0oOat2kjWVsGcNwagdf+fVdslmsc4/ReCsr4rvX0fidLkQ2XjejyAFk6E1Z0cKKVz3zvS18w3lK63TwdZ9qu/ve334aKf24DlIjqSSKr8VAiUJPry7qLdwFqnO+3Y6X26HE99jlH+uQ70ntMJat4b5XGqvtFlec9qxuB1dIr8u6t5y/3kdg3LWLPY9v3QyfGXjrpnwFjL+OsEAyqm1v8i24tXW+yRGy+OMx1JxVN8v94x3mqnXDdEFm8X6atKBpf14RP71Lo7zs9/dL5IWFkQ+nLavMQV26T+jZ/TrnHx5g4oHHVfde8mnVPZ8p6aArCZXlpGumtEA9oBefTuVmuw8PML38PF+7zfvpyniV/NPbAF3lKVQTvP9+srhoI1MaRA9A+P5xyjN9+vgASJj7xZP3fn3x5OH64snD9cWTx3598eTh+uLJw/XFk8d+ffHk4friycP1xZPHfn3x5OH64snD9cWTx3598eTh+uLJw/XFk8d+ffHk4forPNGcTq1F9Tx/gLtS4fYQFXtly71tWPecfpjqPtt9amshd2y+969wJp/GTLs93/urP33izReNLxpfNL5ofNH4ovFF44vGF43/Qhp8FXunyTAvsYIsWwsnT8PAbeiRbX+rG9/q/Fut9q1WeY1Pl/iGd2q1qrx1nJ/iw4VudfBe3fxW1/c3K8738eVUwjP0i/Z3TZTD8Wcl/aL1nW4U6eqykTc1urWJ02Rz+Xhvfpbfk3vreAympInv/t70eLdTXRCfa5V0JX+Dj2Ib50u5i+WteJXEIX3NT5dNnuSH+c58v8tP+fWwirGFCnzbXPbQulGFj/EtvTzj7e8wJvk1oqdgQk7l49/wu/rjNr5cyjB9Q5Lz6yWHW++U+3l+pPZlX7GDfzDfMKD8elrSY+q5y/yUxJfHez/y5RTv5pf09SOBvzTDGsnJfHelVr/VmvM9jIkfFmf873jKL/ES32r8eB9+9Tu4U31kzffGB+Z84Iz426/yZjdfxDv40yo+6fkuP4nu1ivi+kXW/ci5qvYj5+r/UqEq36tiV8Dav8NLam2Up9CX90fy9foMVD8z+0701/jf+D0a9qhg1Y8K9lv160cmdX6iXZX/lHY1f9Cu5SZeZqhLO+gMX5zgU3IRg/ukdfFpn57PaX74ueJ9/n2RgjH+7erY+q/Tx2rrDxSypVVaf0Yh2ek0Lx8eOKJ6nn+ir0SprlU+OFKtUnkUpT98vtWsfBI92YPfah1af+gdgMkb4Em6nF/iX5NUdWeVvqpby1O8wkbmu59ohpTsf9YK7qX+3tCXBvyoAf86xINO1+v1/wYFaNT+8wpQ/0+FOPBtFJ9S6Fp8+iEm/fNC9uekqF77iR3V/qLP/DmTOx9YXG9XPjYge0W/+WucrP+dnPy75/9vn9f/0WofZ7b1KR2TvP1hZn9sqFr5g5b+Rh79cTISxEl6Bh0BOwPNVXZ5csYGDvLrfn6YJ+iGKpC0rFPgobj9/fv33528/H9yCL8S6iruVr5XWo0/Zf5/Iila66OBr/+aNv+7juQHQk3t/8AzVH8mq/8kSPn770zOeIzTJwE+b+arvCApXM3Pm5+J6vlyyrNYSdghP6Bgwy+P2Mj+lpzmx833+dv1FH+/IpE6B0XaPUhky8B/GPrt0uQA95YggOLBb78EOf1rgay2P3r9ev1HuKlJadUj3NT6JGt/Jl1Tfu8DT3/bHO/Py3n8HUTzeoi/r+LXdBn/bwIW5vjjlDcq+O93TXn904yruOthxhVTHmf8s6/+hRnHk3Hy/PKodDgVXr6K8Yl/AA== \ No newline at end of file diff --git a/guides/keto/1-overview/1-installation.md b/_legacy/guides/keto/1-overview/1-installation.md similarity index 100% rename from guides/keto/1-overview/1-installation.md rename to _legacy/guides/keto/1-overview/1-installation.md diff --git a/guides/keto/1-overview/2-configuration.md b/_legacy/guides/keto/1-overview/2-configuration.md similarity index 100% rename from guides/keto/1-overview/2-configuration.md rename to _legacy/guides/keto/1-overview/2-configuration.md diff --git a/guides/keto/1-overview/3-security.md b/_legacy/guides/keto/1-overview/3-security.md similarity index 100% rename from guides/keto/1-overview/3-security.md rename to _legacy/guides/keto/1-overview/3-security.md diff --git a/_legacy/guides/keto/1-overview/README.md b/_legacy/guides/keto/1-overview/README.md new file mode 100644 index 000000000..b79658878 --- /dev/null +++ b/_legacy/guides/keto/1-overview/README.md @@ -0,0 +1,3 @@ +# Quickstart + +This chapter covers how to get started with ORY Keto. diff --git a/guides/keto/2-engines/2-acl.md b/_legacy/guides/keto/2-engines/2-acl.md similarity index 100% rename from guides/keto/2-engines/2-acl.md rename to _legacy/guides/keto/2-engines/2-acl.md diff --git a/guides/keto/2-engines/3-rbac.md b/_legacy/guides/keto/2-engines/3-rbac.md similarity index 100% rename from guides/keto/2-engines/3-rbac.md rename to _legacy/guides/keto/2-engines/3-rbac.md diff --git a/guides/keto/2-engines/4-acp-ory.md b/_legacy/guides/keto/2-engines/4-acp-ory.md similarity index 100% rename from guides/keto/2-engines/4-acp-ory.md rename to _legacy/guides/keto/2-engines/4-acp-ory.md diff --git a/guides/keto/2-engines/5-acp-aws.md b/_legacy/guides/keto/2-engines/5-acp-aws.md similarity index 100% rename from guides/keto/2-engines/5-acp-aws.md rename to _legacy/guides/keto/2-engines/5-acp-aws.md diff --git a/guides/keto/2-engines/README.md b/_legacy/guides/keto/2-engines/README.md similarity index 100% rename from guides/keto/2-engines/README.md rename to _legacy/guides/keto/2-engines/README.md diff --git a/guides/keto/README.md b/_legacy/guides/keto/README.md similarity index 100% rename from guides/keto/README.md rename to _legacy/guides/keto/README.md diff --git a/_legacy/guides/keto/images/rbac.png b/_legacy/guides/keto/images/rbac.png new file mode 100644 index 000000000..b84d8f3bc Binary files /dev/null and b/_legacy/guides/keto/images/rbac.png differ diff --git a/_legacy/guides/keto/images/rbac.xml b/_legacy/guides/keto/images/rbac.xml new file mode 100644 index 000000000..3849469c0 --- /dev/null +++ b/_legacy/guides/keto/images/rbac.xml @@ -0,0 +1 @@ +5ZnLcpswFIafhmUyCIHtLBsnbRftTKZeNFl1ZDgBpYAYIcd2n77CiIss4mYmsWBSbywdXZA+/TpIBwcvs90XTorkO4sgdTw32jn4xvHkbz6Tf5VlX1vm80VtiDmNahPqDCv6B5TRVdYNjaDUKgrGUkEL3RiyPIdQaDbCOdvq1R5Zqj+1IDEYhlVIUtP6k0Yiqa2LwO3sX4HGSfNk5KqSNQl/x5xtcvU8x8OPh19dnJGmL1W/TEjEtj0TvnXwkjMm6lS2W0JasW2w1e0+v1DajptDLl7TQK3TM0k30Iz4MC6xb1gcZgNVfdfB19uEClgVJKxKt3L1pS0RWSpzSCZVd8AF7F4cEmonKgUELAPB97KKauAvFBulHdSw2nYr4TW8k94q4JkyErX6cdt3R0AmFIRhIJ4BZJ2y+FfBSnEZciACTgBCVgAFng4IDwBqofUB+e/AB5/gI70ATJCP71rk45/gk7GIPu4nx6edtw0+wQk+cntFk6ODZhbpzA06d8AzWpaU5eXonnmGAx2NGxhoroYc8zuQWUzvRdW+hEZ5UV0ZQH6wFMYXCQ7GEwkKjOlDJA92Ksu4SFjMcpLedtZrHVAPBuyouO+lH6oql0GVy+XA7lWLQ6YrewIh9upESzaCSVP33G+MFRrqaninQcvZsA0Pm1rNaZjwGETPpZrrwSElgj7r3b+JLjIkV3ls4KP77Nb3juGz0ewcolNCa2Snia7T4INqdWbReabo0Asr8v6qu/rwe3oAr2eJbnsP/a/o+ra0a94m5QQTNj2PafUOgBZ2POaZheUPeEVr+xadA+Gk9u0AXvxGuoemnzgn+16FgtFclL2e7ypDt1X843CLfxRS+0d9Xw/ByUQ9gm6d26m8bveYIQYSZTSfnE+xGpcaCEydAGLnKnQxbiTT1Mlqs36CUIx/SZwfgbF4R/Q+/HmyCeprLyZbR56BLwokpeH4UeGLMb0TtnTMPrOw5gPCsnUPHIiNrtl6crKyGnrAH/4U2MR9xzlkm0HnAsQE4l3Hojvn7U1mu8/R9XG1++aPb/8C \ No newline at end of file diff --git a/guides/oathkeeper/1-overview/1-rules.md b/_legacy/guides/oathkeeper/1-overview/1-rules.md similarity index 100% rename from guides/oathkeeper/1-overview/1-rules.md rename to _legacy/guides/oathkeeper/1-overview/1-rules.md diff --git a/_legacy/guides/oathkeeper/1-overview/2-installation.md b/_legacy/guides/oathkeeper/1-overview/2-installation.md new file mode 100644 index 000000000..6b6f3953e --- /dev/null +++ b/_legacy/guides/oathkeeper/1-overview/2-installation.md @@ -0,0 +1,37 @@ +# Installing ORY Oathkeeper + + + +You can install ORY Oathkeeper by downloading the [binaries](https://github.com/ory/oathkeeper/releases), by using +the precompiled Docker Image available at [Docker Hub](https://hub.docker.com/r/oryd/oathkeeper/), or by +compiling the code yourself. + +## Docker Hub + +The recommended way to install and run ORY Oathkeeper is via docker: + +```sh +$ docker run oryd/oathkeeper: help +``` + +## Binaries + +If you download the binaries, make sure to add them to your path (e.g. `/usr/bin`). Then, run `oathkeeper help` + +## From Source + +To install ORY Oathkeeper from source, you need to have Go 1.10+ installed as well as [go/dep](https://golang.github.io/dep/). +Then, run: + +``` +go get -d -u github.com/ory/oathkeeper +cd $(go env GOPATH)/src/github.com/ory/oathkeeper +OATHKEEPER_LATEST=$(git describe --abbrev=0 --tags) +git checkout $OATHKEEPER_LATEST +dep ensure -vendor-only +go install \ + -ldflags "-X github.com/ory/oathkeeper/cmd.Version=$OATHKEEPER_LATEST -X github.com/ory/oathkeeper/cmd.BuildTime=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/oathkeeper/cmd.GitHash=`git rev-parse HEAD`" \ + github.com/ory/oathkeeper +git checkout master +oathkeeper help +``` diff --git a/_legacy/guides/oathkeeper/1-overview/3-configuration.md b/_legacy/guides/oathkeeper/1-overview/3-configuration.md new file mode 100644 index 000000000..7d2bda642 --- /dev/null +++ b/_legacy/guides/oathkeeper/1-overview/3-configuration.md @@ -0,0 +1,122 @@ +# Configuring and Running ORY Oathkeeper + + + +ORY Oathkeeper has two servers that run on separate ports: + +* The api server: This server is responsible for exposing the management REST API. +* The proxy server: This server is responsible for evaluating access requests and forwarding them to the backend. + +For detailed documentation on the two servers, run `oathkeeper help serve api` and `oathkeeper help serve proxy`. + +ORY Oathkeeper supports two types of storage adapters: + +* In-memory: This adapter does not work with more than one instance ("cluster") and any state is lost after restarting the instance. +* SQL: This adapter works with more than one instance and state is not lost after restarts. + +The SQL adapter supports two DBMS: PostgreSQL 9.6+ and MySQL 5.7+. Please note that +older MySQL versions have issues with the database schema. +For more information [go here](https://github.com/ory/hydra/issues/377). + +ORY Oathkeeper supports various authentication, authorization, and credential strategies. Depending on what strategies +you want to use, you will have to configure more services (e.g. ORY Hydra or ORY Keto). In this tutorial, we will +set up ORY Oathkeeper without any of the other services. Please refer to the [authenticator, authorizer, and credentials +issuer documentation](./1-rules) to see what you need to configure in order to get the strategies you need. + +This guide will: + +1. Download and run a PostgreSQL container in Docker. +2. Download and run ORY Oathkeeper using Docker. + +## Create a Network + +Before we can start, a network must be created which we will attach all our Docker containers to. That way, the containers +can talk to one another. + +``` +$ docker network create oathkeeperguide +``` + +## Start the PostgreSQL Container + +For the purpose of this tutorial, we will use PostgreSQL as a database. As you probably already know, don't run databases in Docker in production! +For the sake of this tutorial however, let's use Docker to quickly deploy the database. + +``` +$ docker run \ + --network oathkeeperguide \ + --name ory-oathkeeper-example--postgres \ + -e POSTGRES_USER=oathkeeper \ + -e POSTGRES_PASSWORD=secret \ + -e POSTGRES_DB=oathkeeper \ + -d postgres:9.6 +``` + +This command wil start a postgres instance with name `ory-oathkeeper-example--postgres`, set up a database called `oathkeeper` +and create a user `oathkeeper` with password `secret`. + +## Run the ORY Oathkeeper API Proxy + +``` +# The database url points us at the postgres instance. This could also be an ephermal in-memory database (`export DATABASE_URL=memory`) +# or a MySQL URI. +$ export DATABASE_URL=postgres://oathkeeper:secret@ory-oathkeeper-example--postgres:5432/oathkeeper?sslmode=disable + +# This pulls the latest image from Docker Hub +$ docker pull oryd/oathkeeper:v1.0.0-beta.5 + +# ORY Oathkeeper does not do magic, it requires conscious decisions, for example running SQL migrations which is required +# when installing a new version of ORY Oathkeeper, or upgrading an existing installation. +# It is the equivalent to `oathkeeper migrate sql postgres://oathkeeper:secret@ory-oathkeeper-example--postgres:5432/oathkeeper?sslmode=disable` +$ docker run -it --rm \ + --network oathkeeperguide \ + oryd/oathkeeper:v1.0.0-beta.5 \ + migrate sql $DATABASE_URL + +Applying `client` SQL migrations... +[...] +Migration successful! + +# Next, let's run the API server! +# +# Please make sure to use your own secret. +$ docker run -d \ + --name ory-oathkeeper-example--oathkeeper-api \ + --network oathkeeperguide \ + -p 4456:4456 \ + -e DATABASE_URL=$DATABASE_URL \ + -e PORT=4456 \ + -e CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET=changemechangemechangemechangemedo \ + oryd/oathkeeper:v1.0.0-beta.5 \ + serve api + +# And the proxy server too - take not that we need to link the proxy serve with the API server! +# +# Please make sure to use your own secret. +$ docker run -d \ + --name ory-oathkeeper-example--oathkeeper-proxy \ + --network oathkeeperguide \ + -p 4455:4455 \ + -e OATHKEEPER_API_URL=http://ory-oathkeeper-example--oathkeeper-api:4456/ \ + -e PORT=4455 \ + -e CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET=changemechangemechangemechangeme \ + oryd/oathkeeper:v1.0.0-beta.5 \ + serve proxy +``` + +Great, both the API and the proxy server are running now! Make sure to check the logs and see if there were +any errors or issues before going to the next steps: + +``` +$ docker logs ory-oathkeeper-example--oathkeeper-api +$ docker logs ory-oathkeeper-example--oathkeeper-proxy +``` + +### Creating your first access rule + +**Sorry, this section is still work in progress.** + +1. Create the rule using `oathkeeper rules import` - need to figure out how to add the file to docker to make this work. +2. Have a rule that works immediately, e.g. protect an Oathkeeper API URL using the Oathkeeper proxy +3. Have two CURL requests where one fails and one passes. Explain why that happens +4. Clean up diff --git a/_legacy/guides/oathkeeper/1-overview/4-deployment.md b/_legacy/guides/oathkeeper/1-overview/4-deployment.md new file mode 100644 index 000000000..ee9f6bdeb --- /dev/null +++ b/_legacy/guides/oathkeeper/1-overview/4-deployment.md @@ -0,0 +1,40 @@ +# Deployment + +**THIS SECTION IS OUTDATED AND TBD** + + + +ORY Oathkeeper is developed as a native cloud application. As such, it is completely stateless, does not require +configuration files and does not persist anything to disk. It is possible to restart ORY Oathkeeper without any side-effects +as ORY Oathkeeper supports graceful HTTP shutdown. + +ORY Oathkeeper works natively with Load Balancers and auto-scaling groups. It is configured using environment variables +and relies on a PostgreSQL / MySQL database instance. To answer access requests quickly, rules are cached in-memory +and refreshed within a given interval (e.g. 30 seconds). + +ORY Oathkeeper has a tiny footprint as it is compiled to native bytecode. The docker image is a mere 5 MB, the memory +footprint similarly low and the CPU usage is, on average, close to zero. + +Thus, two possible deployment layouts are possible for ORY Oathkeeper, which are covered in the next two sections. + +## Gateway + +In the gateway deployment scenario, ORY Oathkeeper is the single point of entry for all API endpoints. Oathkeeper might +sit behind a load balancer, but is generally deployed in front of the API router. Any incoming requests first +pass ORY Oathkeeper, then the API router, and finally reach the API endpoint service. + +![Gateway Oathkeeper Deployment Layout](../images/gateway_deployment.svg) + +The advantage of this deployment layout is that there is only one deployment of ORY Oathkeeper that needs to be maintained. +The disadvantage is that it is not possible to talk to API endpoints directly without going through the full +reverse proxy chain. + +## Sidecar + +In the sidecar deployment scenario, ORY Oathkeeper is deployed alongside each API endpoint service, probably even +within the same logical unit (e.g. Docker Container or VM) as the service itself. + +![Gateway Oathkeeper Deployment Layout](../images/sidecar_deployment.svg) + +The advantage of this deployment layout is that requests to the API endpoints are possible without passing through +the API router. The disadvantage is that multiple instances of ORY Oathkeeper have to be maintained. diff --git a/_legacy/guides/oathkeeper/1-overview/5-proxy-judge.md b/_legacy/guides/oathkeeper/1-overview/5-proxy-judge.md new file mode 100644 index 000000000..b0562add0 --- /dev/null +++ b/_legacy/guides/oathkeeper/1-overview/5-proxy-judge.md @@ -0,0 +1,96 @@ +# Proxy & Judge + +ORY Oathkeeper has two methods of answering access requests: proxy and judge. + +## Proxy + +The proxy process (`oathkeeper serve proxy`) forwards requests to the upstream server, defined in the rule, +if the request is allowed. If the request is not allowed, ORY Oathkeeper does not forward the request and instead +returns an error message. + +Assuming you are making the following request + +``` +GET /my-service/whatever HTTP/1.1 +Host: oathkeeper-proxy:4455 +Authorization: bearer some-token +``` + +and you have the following rule defined (which allows this request) + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://oathkeeper-proxy:4455/my-service/whatever", + "methods": [ "GET" ] + }, + "authenticators": [{ "handler": "noop" }], + "authorizer": { "handler": "allow" } + "credentials_issuer": { "handler": "noop" } +} +``` + +then the request will be forwarded by ORY Oathkeeper as follows: + + +``` +GET /my-service/whatever HTTP/1.1 +Host: my-backend-service:4455 +Authorization: bearer some-token +``` + +The response of this request will then be sent to the client that made the request to ORY Oathkeeper. + +## Judge + +The `/judge` endpoint served by `oathkeeper serve api` works in a similar fashion to the proxy, with the difference +of not forwarding the request to the upstream server but instead returning a response which indicates if the request +should be allowed to proceed or not. + +Assuming you are making the following request + +``` +GET /judge/my-service/whatever HTTP/1.1 +Host: oathkeeper-api:4456 +Authorization: bearer some-token +``` + +and you have the following rule defined (which allows this request) + +``` +{ + "id": "some-id", + "upstream": { "url": "" }, + "match": { + "url": "http://oathkeeper-api:4456/my-service/whatever", + "methods": [ "GET" ] + }, + "authenticators": [{ "handler": "noop" }], + "authorizer": { "handler": "allow" } + "credentials_issuer": { "handler": "noop" } +} +``` + +then this endpoint will directly respond with HTTP Status Code 200: + + +``` +HTTP/1.1 200 OK +Authorization: bearer-sometoken +``` + +If any other status code is returned, the request must not be allowed, for example: + +``` +HTTP/1.1 401 OK +Content-Length: 0 +Connection: Closed +``` + +The judge endpoint automatically strips the `/judge` path prefix from the request URL so you don't need to add that +prefix to the `match.url` value of your rule (use `http://oathkeeper-api:4456/my-service/whatever` +instead of `http://oathkeeper-api:4456/judge/my-service/whatever`). diff --git a/_legacy/guides/oathkeeper/1-overview/README.md b/_legacy/guides/oathkeeper/1-overview/README.md new file mode 100644 index 000000000..c87791d0d --- /dev/null +++ b/_legacy/guides/oathkeeper/1-overview/README.md @@ -0,0 +1,3 @@ +# Quickstart + +This chapter covers how to get started with ORY Oathkeeper. diff --git a/_legacy/guides/oathkeeper/README.md b/_legacy/guides/oathkeeper/README.md new file mode 100644 index 000000000..c5abae82f --- /dev/null +++ b/_legacy/guides/oathkeeper/README.md @@ -0,0 +1,10 @@ +# Introduction + +Welcome to the ORY Oathkeeper documentation! + +ORY Oathkeeper is a reverse proxy which evaluates incoming HTTP requests based on a set of rules (called "access rules"). +The feature set ORY Oathkeeper implements is referred to as an Identity and Access Proxy (IAP) in the context +of [BeyondCorp and ZeroTrust](https://www.beyondcorp.com). + +In principal, ORY Oathkeeper inspects the `Authorization` header and the full request url (e.g. `https://mydomain.com/api/foo`) +of incoming HTTP requests, applies a set of rules, and either grants access to the requested url or denies access. diff --git a/_legacy/guides/oathkeeper/diagrams/api_router_layout.mermaid b/_legacy/guides/oathkeeper/diagrams/api_router_layout.mermaid new file mode 100644 index 000000000..5e8988123 --- /dev/null +++ b/_legacy/guides/oathkeeper/diagrams/api_router_layout.mermaid @@ -0,0 +1,8 @@ +graph LR + + UA[User Agent] --- FW[ORY Oathkeeper] + + FW --- APIR[API Router] + APIR --- API1[API Endpoint 1] + APIR --- API2[API Endpoint 2] + APIR --- API3[API Endpoint 3] diff --git a/_legacy/guides/oathkeeper/diagrams/invalid_access_request.mermaid b/_legacy/guides/oathkeeper/diagrams/invalid_access_request.mermaid new file mode 100644 index 000000000..230d3ef0c --- /dev/null +++ b/_legacy/guides/oathkeeper/diagrams/invalid_access_request.mermaid @@ -0,0 +1,15 @@ +sequenceDiagram + participant UA as User Agent + participant FW as ORY Oathkeeper + participant OA2 as ORY Hydra + participant API as API Endpoint + + UA->>FW: HTTP Request with opaque access token + activate FW + FW->>OA2: Ask for access request validation + activate OA2 + OA2->>OA2: Process access request + OA2->>FW: Forbid access request + deactivate OA2 + FW->>UA: HTTP 403 Forbidden + deactivate FW diff --git a/_legacy/guides/oathkeeper/diagrams/sidecar_layout.mermaid b/_legacy/guides/oathkeeper/diagrams/sidecar_layout.mermaid new file mode 100644 index 000000000..28d7715cf --- /dev/null +++ b/_legacy/guides/oathkeeper/diagrams/sidecar_layout.mermaid @@ -0,0 +1,18 @@ +graph LR + + UA[User Agent] --- APIR[API Router] + + APIR --- FW1[ORY Oathkeeper] + subgraph Service 1 + FW1 --- API1[API Endpoint 1] + end + + APIR --- FW2[ORY Oathkeeper] + subgraph Service 2 + FW2 --- API2[API Endpoint 2] + end + + APIR --- FW3[ORY Oathkeeper] + subgraph Service 3 + FW3 --- API3[API Endpoint 3] + end \ No newline at end of file diff --git a/_legacy/guides/oathkeeper/diagrams/valid_access_request.mermaid b/_legacy/guides/oathkeeper/diagrams/valid_access_request.mermaid new file mode 100644 index 000000000..392c852f0 --- /dev/null +++ b/_legacy/guides/oathkeeper/diagrams/valid_access_request.mermaid @@ -0,0 +1,21 @@ +sequenceDiagram + participant UA as User Agent + participant FW as ORY Oathkeeper + participant OA2 as ORY Hydra + participant API as API Endpoint + + UA->>FW: HTTP Request with opaque access token + activate FW + FW->>OA2: Ask for access request validation + activate OA2 + OA2->>OA2: Process access request + OA2->>FW: Approve request and return identity information + deactivate OA2 + FW->>FW: Generate JWT ID Token + FW->>API: Forward request with ID Token + activate API + API->>API: Process request with user info from ID token + API->>FW: Return response + deactivate API + FW->>UA: Forward response + deactivate FW diff --git a/_legacy/guides/oathkeeper/images/api_router_layout.svg b/_legacy/guides/oathkeeper/images/api_router_layout.svg new file mode 100644 index 000000000..fcdd0d08a --- /dev/null +++ b/_legacy/guides/oathkeeper/images/api_router_layout.svg @@ -0,0 +1,360 @@ +
ORY Gatekeeper
Access request validation
ORY Oathkeeper
ORY Hydra
User Agent
API Endpoint
\ No newline at end of file diff --git a/_legacy/guides/oathkeeper/images/gateway_deployment.svg b/_legacy/guides/oathkeeper/images/gateway_deployment.svg new file mode 100644 index 000000000..30f82446c --- /dev/null +++ b/_legacy/guides/oathkeeper/images/gateway_deployment.svg @@ -0,0 +1,360 @@ +
User Agent
ORY Oathkeeper
API Router
API Endpoint 1
API Endpoint 2
API Endpoint 3
\ No newline at end of file diff --git a/_legacy/guides/oathkeeper/images/id_token.svg b/_legacy/guides/oathkeeper/images/id_token.svg new file mode 100644 index 000000000..571cf01f7 --- /dev/null +++ b/_legacy/guides/oathkeeper/images/id_token.svg @@ -0,0 +1,360 @@ +User AgentORY OathkeeperAPI EndpointAuthorization: bearer Opaque.TokenAuthorization: bearer JWT.ID.TokenDecodes & validates JWT.ID.Token using Oathkeeper's public keyUser AgentORY OathkeeperAPI Endpoint \ No newline at end of file diff --git a/_legacy/guides/oathkeeper/images/insecure-connection.jpg b/_legacy/guides/oathkeeper/images/insecure-connection.jpg new file mode 100644 index 000000000..8de1ceb0f Binary files /dev/null and b/_legacy/guides/oathkeeper/images/insecure-connection.jpg differ diff --git a/_legacy/guides/oathkeeper/images/invalid_access_request.svg b/_legacy/guides/oathkeeper/images/invalid_access_request.svg new file mode 100644 index 000000000..f6d2f3e96 --- /dev/null +++ b/_legacy/guides/oathkeeper/images/invalid_access_request.svg @@ -0,0 +1,360 @@ +User AgentORY OathkeeperORY HydraAPI EndpointHTTP Request with opaque access tokenAsk for access request validationProcess access requestForbid access requestHTTP 403 ForbiddenUser AgentORY OathkeeperORY HydraAPI Endpoint \ No newline at end of file diff --git a/_legacy/guides/oathkeeper/images/sidecar_deployment.svg b/_legacy/guides/oathkeeper/images/sidecar_deployment.svg new file mode 100644 index 000000000..31c2b07c0 --- /dev/null +++ b/_legacy/guides/oathkeeper/images/sidecar_deployment.svg @@ -0,0 +1,360 @@ +
Service 3
Service 2
Service 1
ORY Oathkeeper
API Endpoint 3
ORY Oathkeeper
API Endpoint 2
ORY Oathkeeper
API Endpoint 1
User Agent
API Router
\ No newline at end of file diff --git a/_legacy/guides/oathkeeper/images/valid_access_request.svg b/_legacy/guides/oathkeeper/images/valid_access_request.svg new file mode 100644 index 000000000..fb4c54afe --- /dev/null +++ b/_legacy/guides/oathkeeper/images/valid_access_request.svg @@ -0,0 +1,360 @@ +User AgentORY OathkeeperORY HydraAPI EndpointHTTP Request with opaque access tokenAsk for access request validationProcess access requestApprove request and return identity informationGenerate JWT ID TokenForward request with ID TokenProcess request with user info from ID tokenReturn responseForward responseUser AgentORY OathkeeperORY HydraAPI Endpoint \ No newline at end of file diff --git a/_legacy/guides/package.json b/_legacy/guides/package.json new file mode 100644 index 000000000..9664f260a --- /dev/null +++ b/_legacy/guides/package.json @@ -0,0 +1,4 @@ +{ + "dependencies": { + } +} diff --git a/guides/performance/1-hydra.md b/_legacy/guides/performance/1-hydra.md similarity index 100% rename from guides/performance/1-hydra.md rename to _legacy/guides/performance/1-hydra.md diff --git a/_legacy/guides/performance/README.md b/_legacy/guides/performance/README.md new file mode 100644 index 000000000..d07965fe1 --- /dev/null +++ b/_legacy/guides/performance/README.md @@ -0,0 +1,8 @@ +# Performance Benchmarks + +Our ecosystem is not another over-hyped, oversold technology stack. Our technology is truly built with 12 factor principles +in mind, and the technology's performance metrics are as good as it gets. + +To prove this to you, we run an automated benchmark test suite **on every product on every push to master** +(well, almost - benchmarks for ORY Oathkeeper and ORY Keto will follow soon). You can +select the product you would like to know more about from the navigation on the left. diff --git a/_legacy/guides/telemetry/README.md b/_legacy/guides/telemetry/README.md new file mode 100644 index 000000000..29dcfd9f2 --- /dev/null +++ b/_legacy/guides/telemetry/README.md @@ -0,0 +1,79 @@ +# Telemetry + +Our goal is to have the fastest and most reliable open source services. To achieve this goal, +we collect metrics on endpoint performance and send a **fully anonymized** telemetry report +("anonymous usage statistics") to our servers. This data helps us understand how changes impact performance +and stability of ORY Hydra and identify potential issues. + +We are committed to full transparency on what data we transmit why and how. The source code of the telemetry package is completely open source +and located [here](https://github.com/ory/metrics-middleware). If you do not wish to help us improving ORY Hydra +by sharing telemetry data, it is possible to [turn this feature off](#disabling-telemetry). + +To protect your privacy, we filter out any data that could identify you or your users. We are taking the following +measures to protect your privacy: + +1. We only transmit information on how often endpoints are requested, how fast they respond and what HTTP status code was sent. +2. We filter out any query parameters, headers, response and request bodies and path parameters. A full list of transmitted +URL paths is listed in section [Request telemetry](#request-telemetry). +4. **We are unable to see or store the IP address of your host**, as the +[IP is set to `0.0.0.0`](https://github.com/ory/hydra/tree/master/metrics/middleware.go) when transmitting data to our metrics aggregator. +5. We do not transmit any environment information from the host, except: + * Operating system id (windows, linux, osx) + * The target architecture (amd64, darwin, ...) + * Number of CPUs available + * Build time, hash and version of ORY Hydra + * Memory consumption of ORY Hydra's process + +The information is stored in an aggregated format without any personally identifiable information. + +## Identification + +To identify an installation and group together clusters, we create a SHA-256 hash of the Issuer URL for identification. +Additionally, each running instance is identified using an unique identifier which is set every time ORY Hydra starts. The identifier +is a Universally Unique Identifier (V4) and is thus a cryptographically safe random string. Identification is triggered +when we are confident that the instance is not a test instance (e.g. one of the tutorials or a local installation). + +We collect the following system metrics: + +* `goarch`: The target architecture of the ORY Hydra binary. +* `goos`: The target system of the ORY Hydra binary. +* `numCpu`: The number of CPUs available. +* `runtimeVersion`: The go version used to create the binary. +* `version`: The version of this binary. +* `hash`: The git hash of this binary. +* `buildTime`: The build time of this binary. + +## Request telemetry + +The ip addresses of both host and client are anonymized to `0.0.0.0`. Any identifiable information in the URL path and query is hashed with +sha256 using a randomly assigned uuid v4 salt: + +* `/clients/foo` with salt `ABCDEFGH` becomes `/clients/sha256("foo|ABCDEFGH")`: `/clients/0301424a80469ad03a208de925563a97ec6ab2f9dc7a2ad71b2ded85a7f7a7af` +* `/policies?owner=foo` with salt `ABCDEFGH` becomes `/policies?owner=sha256("foo|ABCDEFGH")`: `/policies?owner=0301424a80469ad03a208de925563a97ec6ab2f9dc7a2ad71b2ded85a7f7a7af`). + +## Code + +The full code-base is [open sourced](https://github.com/ory/metrics-middleware). + +## Data processing + +Once the data was transmitted to [Segment.com](http://segment.com/) it is aggregated and then fed to an encrypted AWS S3 bucket. + +We analyze the data with the following goals: + +1. Be able to say how many production deployments exist. +- Understand which features are used and how. +- Understand how much throughput deployments handle. +- Evaluate how frequently specific features (e.g. policies) are used. +- Detect issues introduced by new features (e.g. buggy releases). For example: + - After release 0.X.Y, all instances show 25% increase in response times for Warden API calls. +- Identify real-world problems caused by things such as slow queries. For example: + - Searching for policies by owners takes causes high response times. + - Running the deployment for several months and high traffic causes slow response times. + +## Disabling telemetry + +You can opt out of telemetry reports with the `--disable-telemetry` flag and also by +setting environment variable `DISABLE_TELEMETRY=1`. + +Disabling telemetry does not have any downsides, except for us not being able to improve the product. diff --git a/_legacy/guides/yarn.lock b/_legacy/guides/yarn.lock new file mode 100644 index 000000000..fb57ccd13 --- /dev/null +++ b/_legacy/guides/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + diff --git a/package-lock.json b/_legacy/package-lock.json similarity index 100% rename from package-lock.json rename to _legacy/package-lock.json diff --git a/_legacy/package.json b/_legacy/package.json new file mode 100644 index 000000000..ba4bf87f9 --- /dev/null +++ b/_legacy/package.json @@ -0,0 +1,17 @@ +{ + "dependencies": { + "gitbook": "^3.2.3", + "gitbook-cli": "^2.3.2", + "npm-run-all": "^4.1.2" + }, + "scripts": { + "postinstall": "npm-run-all install:*", + "build": "npm-run-all build:*", + "install:faq": "cd faq && yarn && gitbook install", + "install:guides": "cd guides && yarn && gitbook install", + "build:guides": "cd guides && gitbook build . ../build/guides", + "build:faq": "cd faq && gitbook build . ../build/faq", + "start:guides": "cd guides && gitbook serve -http .", + "start:faq": "cd faq && gitbook serve -http ." + } +} diff --git a/_legacy/yarn.lock b/_legacy/yarn.lock new file mode 100644 index 000000000..9bf1c5abd --- /dev/null +++ b/_legacy/yarn.lock @@ -0,0 +1,4805 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +JSONStream@~1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.2.tgz#c102371b6ec3a7cf3b847ca00c20bb0fce4c6dea" + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +abab@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" + +abbrev@1, abbrev@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +abbrev@~1.0.7, abbrev@~1.0.9: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + +acorn-globals@^1.0.4: + version "1.0.9" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-1.0.9.tgz#55bb5e98691507b74579d0513413217c380c54cf" + dependencies: + acorn "^2.1.0" + +acorn@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-0.9.0.tgz#67728e0acad6cc61dfb901c121837694db5b926b" + +acorn@^2.1.0, acorn@^2.4.0: + version "2.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7" + +agent-base@4, agent-base@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.0.tgz#9838b5c3392b962bad031e6a4c5e1024abec45ce" + dependencies: + es6-promisify "^5.0.0" + +agentkeepalive@^3.3.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.4.0.tgz#92487926ec1a93100a89a8a46a6b2d82513543ce" + dependencies: + humanize-ms "^1.2.1" + +ajv@^4.9.1: + version "4.11.8" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +ajv@^5.1.0: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +ansi-align@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" + dependencies: + string-width "^2.0.0" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0, ansi-regex@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.2.0, ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +ansi@^0.3.0, ansi@~0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/ansi/-/ansi-0.3.1.tgz#0c42d4fb17160d5a9af1e484bace1c66922c1b21" + +ansicolors@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" + +ansistyles@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/ansistyles/-/ansistyles-0.1.3.tgz#5de60415bda071bb37127854c864f41b23254539" + +anymatch@^1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" + dependencies: + micromatch "^2.1.5" + normalize-path "^2.0.0" + +aproba@^1.0.3, aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +aproba@~1.0.1, aproba@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.0.4.tgz#2713680775e7614c8ba186c065d4e2e52d1072c0" + +aproba@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1" + +archy@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" + +are-we-there-yet@~1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +array-difference@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-difference/-/array-difference-0.0.1.tgz#c7cafd9b54b35eb82f72e7ba319e938a3fd32b07" + +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + +array-index@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-index/-/array-index-1.0.0.tgz#ec56a749ee103e4e08c790b9c353df16055b97f9" + dependencies: + debug "^2.2.0" + es6-symbol "^3.0.2" + +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +asap@^2.0.0, asap@^2.0.3, asap@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + +asciidoctor.js@1.5.5-1: + version "1.5.5-1" + resolved "https://registry.yarnpkg.com/asciidoctor.js/-/asciidoctor.js-1.5.5-1.tgz#bdb5322a91a1f005a3ea7fc2f9e11e18ab152f46" + dependencies: + opal-npm-wrapper "^0.9.0-beta2" + xmlhttprequest "~1.7.0" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +astw@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/astw/-/astw-1.3.0.tgz#015774a6427ad3b9ec46d7a2b41ae73dac624ca5" + dependencies: + esprima "^2.1.0" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async-some@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/async-some/-/async-some-1.0.2.tgz#4d8a81620d5958791b5b98f802d3207776e95509" + dependencies: + dezalgo "^1.0.2" + +async@^0.9.0: + version "0.9.2" + resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" + +async@^2.0.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" + dependencies: + lodash "^4.14.0" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + +aws4@^1.2.1, aws4@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +bash-color@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/bash-color/-/bash-color-0.0.4.tgz#e9be8ce33540cada4881768c59bd63865736e913" + +batch@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.5.3.tgz#3f3414f380321743bfc1042f9a83ff1d5824d464" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +bl@^1.0.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.1.tgz#cac328f7bee45730d404b692203fcb590e172d5e" + dependencies: + readable-stream "^2.0.5" + +bl@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/bl/-/bl-1.1.2.tgz#fdca871a99713aa00d19e3bbba41c44787a65398" + dependencies: + readable-stream "~2.0.5" + +block-stream@*, block-stream@0.0.9: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +bluebird@^3.5.0, bluebird@^3.5.1, bluebird@~3.5.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +body-parser@~1.14.0: + version "1.14.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.14.2.tgz#1015cb1fe2c443858259581db53332f8d0cf50f9" + dependencies: + bytes "2.2.0" + content-type "~1.0.1" + debug "~2.2.0" + depd "~1.1.0" + http-errors "~1.3.1" + iconv-lite "0.4.13" + on-finished "~2.3.0" + qs "5.2.0" + raw-body "~2.1.5" + type-is "~1.6.10" + +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +boom@4.x.x: + version "4.3.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-4.3.1.tgz#4f8a3005cb4a7e3889f749030fd25b96e01d2e31" + dependencies: + hoek "4.x.x" + +boom@5.x.x: + version "5.2.0" + resolved "https://registry.yarnpkg.com/boom/-/boom-5.2.0.tgz#5dd9da6ee3a5f302077436290cb717d3f4a54e02" + dependencies: + hoek "4.x.x" + +boxen@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" + dependencies: + ansi-align "^2.0.0" + camelcase "^4.0.0" + chalk "^2.0.1" + cli-boxes "^1.0.0" + string-width "^2.0.0" + term-size "^1.2.0" + widest-line "^2.0.0" + +brace-expansion@^1.0.0, brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +buffer-shims@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + +builtin-modules@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +builtins@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-0.0.7.tgz#355219cd6cf18dbe7c01cc7fd2dce765cfdc549a" + +builtins@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" + +bytes@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.2.0.tgz#fd35464a403f6f9117c2de3609ecff9cae000588" + +bytes@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339" + +cacache@^10.0.0: + version "10.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" + dependencies: + bluebird "^3.5.1" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^2.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^5.2.4" + unique-filename "^1.1.0" + y18n "^4.0.0" + +cacache@^9.2.9: + version "9.3.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-9.3.0.tgz#9cd58f2dd0b8c8cacf685b7067b416d6d3cf9db1" + dependencies: + bluebird "^3.5.0" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^1.3.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.1" + ssri "^4.1.6" + unique-filename "^1.1.0" + y18n "^3.2.1" + +cacache@~9.2.9: + version "9.2.9" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-9.2.9.tgz#f9d7ffe039851ec94c28290662afa4dd4bb9e8dd" + dependencies: + bluebird "^3.5.0" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^1.3.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.1" + ssri "^4.1.6" + unique-filename "^1.1.0" + y18n "^3.2.1" + +call-limit@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/call-limit/-/call-limit-1.1.0.tgz#6fd61b03f3da42a2cd0ec2b60f02bd0e71991fea" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +camelcase@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + +camelcase@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + +capture-stack-trace@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chalk@^1.0.0, chalk@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.1, chalk@^2.1.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +char-spinner@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/char-spinner/-/char-spinner-1.0.1.tgz#e6ea67bd247e107112983b7ab0479ed362800081" + +cheerio@0.20.0, "cheerio@^0.20.0 && >=0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.20.0.tgz#5c710f2bab95653272842ba01c6ea61b3545ec35" + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "~3.8.1" + lodash "^4.1.0" + optionalDependencies: + jsdom "^7.0.2" + +chmodr@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/chmodr/-/chmodr-1.0.2.tgz#04662b932d0f02ec66deaa2b0ea42811968e3eb9" + +chokidar@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.5.0.tgz#7a5f1a72e6ee3e1daffdae74832e8eb28ee2f19a" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +chokidar@^1.6.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +chownr@^1.0.1, chownr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + +clean-css@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-1.1.7.tgz#601ef9cf7642b982cb33efc9488a6444c986686e" + dependencies: + commander "2.0.x" + +cli-boxes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" + +cli-color@^0.3.2: + version "0.3.3" + resolved "https://registry.yarnpkg.com/cli-color/-/cli-color-0.3.3.tgz#12d5bdd158ff8a0b0db401198913c03df069f6f5" + dependencies: + d "~0.1.1" + es5-ext "~0.10.6" + memoizee "~0.3.8" + timers-ext "0.1" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +cliui@^3.0.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + wrap-ansi "^2.0.0" + +clone@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" + +cmd-shim@~2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-2.0.2.tgz#6fcbda99483a8fd15d7d30a196ca69d688a2efdb" + dependencies: + graceful-fs "^4.1.2" + mkdirp "~0.5.0" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +color-convert@^1.9.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" + dependencies: + color-name "^1.1.1" + +color-name@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +columnify@~1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" + dependencies: + strip-ansi "^3.0.0" + wcwidth "^1.0.0" + +combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" + dependencies: + delayed-stream "~1.0.0" + +commander@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.0.0.tgz#d1b86f901f8b64bd941bdeadaf924530393be928" + +commander@2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.11.0.tgz#157152fd1e7a6c8d98a5b715cf376df928004563" + +commander@2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + +commander@^2.9.0: + version "2.14.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.4.6, concat-stream@^1.5.0, concat-stream@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.1.tgz#261b8f518301f1d834e36342b9fea095d2620a26" + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +config-chain@~1.1.10, config-chain@~1.1.11: + version "1.1.11" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2" + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + +configstore@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90" + dependencies: + dot-prop "^4.1.0" + graceful-fs "^4.1.2" + make-dir "^1.0.0" + unique-string "^1.0.0" + write-file-atomic "^2.0.0" + xdg-basedir "^3.0.0" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +content-type@~1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +core-util-is@1.0.2, core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cp@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/cp/-/cp-0.2.0.tgz#a0874183a09e885eb72792582ab68ce3932b135d" + +cpr@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/cpr/-/cpr-1.1.1.tgz#039dc5cc9aae9c20996bb18110d602a792dca70b" + dependencies: + graceful-fs "~4.1.2" + mkdirp "~0.5.0" + rimraf "~2.4.3" + +crc@3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.0.tgz#4258e351613a74ef1153dfcb05e820c3e9715d7f" + +create-error-class@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6" + dependencies: + capture-stack-trace "^1.0.0" + +cross-spawn-async@^2.1.8: + version "2.2.5" + resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc" + dependencies: + lru-cache "^4.0.0" + which "^1.2.8" + +cross-spawn@^5.0.1, cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +cryptiles@3.x.x: + version "3.1.2" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-3.1.2.tgz#a89fbb220f5ce25ec56e8c4aa8a4fd7b5b0d29fe" + dependencies: + boom "5.x.x" + +crypto-random-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" + +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" + +cssom@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.1.tgz#c9e37ef2490e64f6d1baa10fda852257082c25d3" + +cssom@0.3.x, "cssom@>= 0.3.0 < 0.4.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.2.tgz#b8036170c79f07a90ff2f16e22284027a243848b" + +"cssstyle@>= 0.2.29 < 0.3.0": + version "0.2.37" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-0.2.37.tgz#541097234cb2513c83ceed3acddc27ff27987d54" + dependencies: + cssom "0.3.x" + +cyclist@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + +d@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" + dependencies: + es5-ext "^0.10.9" + +d@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/d/-/d-0.1.1.tgz#da184c535d18d8ee7ba2aa229b914009fae11309" + dependencies: + es5-ext "~0.10.2" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +datauri@~0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/datauri/-/datauri-0.2.1.tgz#f4e8addbb3e54e3dc12d1c88543b8b0b1bf692fa" + dependencies: + mimer "*" + templayed "*" + +debug@3.1.0, debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da" + dependencies: + ms "0.7.1" + +debuglog@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" + +decamelize@^1.0.0, decamelize@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +deep-extend@^0.4.0, deep-extend@~0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + dependencies: + clone "^1.0.2" + +define-properties@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" + dependencies: + foreach "^2.0.5" + object-keys "^1.0.8" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +depd@~1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + +destroy@1.0.4, destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + +detect-indent@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +dezalgo@^1.0.0, dezalgo@^1.0.1, dezalgo@^1.0.2, dezalgo@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456" + dependencies: + asap "^2.0.0" + wrappy "1" + +direction@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/direction/-/direction-0.1.5.tgz#ce5d797f97e26f8be7beff53f7dc40e1c1a9ec4c" + +dom-serializer@0, dom-serializer@0.1.0, dom-serializer@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domelementtype@1, domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" + dependencies: + domelementtype "1" + +domhandler@^2.3.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259" + dependencies: + domelementtype "1" + +domutils@1.5, domutils@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-prop@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" + dependencies: + is-obj "^1.0.0" + +duplexer3@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" + +duplexer@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + +duplexify@^3.4.2, duplexify@^3.5.3: + version "3.5.4" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.4.tgz#4bb46c1796eabebeec4ca9a2e66b808cb7a3d8b4" + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +editor@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/editor/-/editor-1.0.0.tgz#60c7f87bd62bcc6a894fa8ccd6afb7823a24f742" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + dependencies: + once "^1.4.0" + +entities@1.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" + +entities@^1.1.1, entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +err-code@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" + +"errno@>=0.1.1 <0.2.0-0": + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" + dependencies: + is-arrayish "^0.2.1" + +error@7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02" + dependencies: + string-template "~0.2.1" + xtend "~4.0.0" + +es-abstract@^1.4.3: + version "1.10.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + +es-to-primitive@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" + dependencies: + is-callable "^1.1.1" + is-date-object "^1.0.1" + is-symbol "^1.0.1" + +es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.11, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.5, es5-ext@~0.10.6: + version "0.10.39" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.39.tgz#fca21b67559277ca4ac1a1ed7048b107b6f76d87" + dependencies: + es6-iterator "~2.0.3" + es6-symbol "~3.1.1" + +es6-iterator@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-0.1.3.tgz#d6f58b8c4fc413c249b4baa19768f8e4d7c8944e" + dependencies: + d "~0.1.1" + es5-ext "~0.10.5" + es6-symbol "~2.0.1" + +es6-iterator@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-promise@^4.0.3: + version "4.2.4" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.4.tgz#dc4221c2b16518760bd8c39a52d8f356fc00ed29" + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + dependencies: + es6-promise "^4.0.3" + +es6-symbol@^3.0.2, es6-symbol@^3.1.1, es6-symbol@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" + dependencies: + d "1" + es5-ext "~0.10.14" + +es6-symbol@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-2.0.1.tgz#761b5c67cfd4f1d18afb234f691d678682cb3bf3" + dependencies: + d "~0.1.1" + es5-ext "~0.10.5" + +es6-weak-map@~0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-0.1.4.tgz#706cef9e99aa236ba7766c239c8b9e286ea7d228" + dependencies: + d "~0.1.1" + es5-ext "~0.10.6" + es6-iterator "~0.1.3" + es6-symbol "~2.0.1" + +escape-html@^1.0.3, escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escodegen@^1.4.1, escodegen@^1.6.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.9.1.tgz#dbae17ef96c8e4bedb1356f4504fa4cc2f7cb7e2" + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + +esprima@^2.1.0: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +esprima@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" + +esprima@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" + +estraverse@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +etag@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.7.0.tgz#03d30b5f67dd6e632d2945d30d6652731a34d5d8" + +event-emitter@~0.3.4: + version "0.3.5" + resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" + dependencies: + d "1" + es5-ext "~0.10.14" + +event-stream@~3.3.0: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + +fast-deep-equal@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +faye-websocket@~0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + dependencies: + websocket-driver ">=0.5.1" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +flush-write-stream@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.4" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~1.0.0-rc3, form-data@~1.0.0-rc4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-1.0.1.tgz#ae315db9a4907fa065502304a66d7733475ee37c" + dependencies: + async "^2.0.1" + combined-stream "^1.0.5" + mime-types "^2.1.11" + +form-data@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.0.0.tgz#6f0aebadcc5da16c13e1ecc11137d85f9b883b25" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.11" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +form-data@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" + dependencies: + asynckit "^0.4.0" + combined-stream "1.0.6" + mime-types "^2.1.12" + +fresh-require@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fresh-require/-/fresh-require-1.0.3.tgz#5a0613c023a2b0dce4373864ba539bade45122c3" + dependencies: + acorn "^0.9.0" + astw "^1.2.0" + escodegen "^1.4.1" + is-require "0.0.1" + resolve "^1.0.0" + shallow-copy "0.0.1" + sleuth "^0.1.1" + through2 "^0.6.3" + +fresh@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f" + +from2@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-1.3.0.tgz#88413baaa5f9a597cfde9221d86986cd3c061dfd" + dependencies: + inherits "~2.0.1" + readable-stream "~1.1.10" + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + +front-matter@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/front-matter/-/front-matter-2.3.0.tgz#7203af896ce357ee04e2aa45169ea91ed7f67504" + dependencies: + js-yaml "^3.10.0" + +fs-extra@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" + dependencies: + graceful-fs "^4.1.2" + jsonfile "^3.0.0" + universalify "^0.1.0" + +fs-vacuum@~1.2.10, fs-vacuum@~1.2.9: + version "1.2.10" + resolved "https://registry.yarnpkg.com/fs-vacuum/-/fs-vacuum-1.2.10.tgz#b7629bec07a4031a2548fdf99f5ecf1cc8b31e36" + dependencies: + graceful-fs "^4.1.2" + path-is-inside "^1.0.1" + rimraf "^2.5.2" + +fs-write-stream-atomic@^1.0.8, fs-write-stream-atomic@~1.0.10, fs-write-stream-atomic@~1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.39" + +fstream-ignore@^1.0.0, fstream-ignore@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream-npm@~1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/fstream-npm/-/fstream-npm-1.0.7.tgz#7ed0d1ac13d7686dd9e1bf6ceb8be273bf6d2f86" + dependencies: + fstream-ignore "^1.0.0" + inherits "2" + +fstream-npm@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fstream-npm/-/fstream-npm-1.1.1.tgz#6b9175db6239a83d8209e232426c494dbb29690c" + dependencies: + fstream-ignore "^1.0.0" + inherits "2" + +fstream-npm@~1.2.0, fstream-npm@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/fstream-npm/-/fstream-npm-1.2.1.tgz#08c4a452f789dcbac4c89a4563c902b2c862fd5b" + dependencies: + fstream-ignore "^1.0.0" + inherits "2" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2, fstream@~1.0.10, fstream@~1.0.11, fstream@~1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +function-bind@^1.0.2, function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +gauge@~1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-1.2.7.tgz#e9cec5483d3d4ee0ef44b60a7d99e4935e136d93" + dependencies: + ansi "^0.3.0" + has-unicode "^2.0.0" + lodash.pad "^4.1.0" + lodash.padend "^4.1.0" + lodash.padstart "^4.1.0" + +gauge@~2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.6.0.tgz#d35301ad18e96902b4751dcbbe40f4218b942a46" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-color "^0.1.7" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gauge@~2.7.1, gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + +genfun@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/genfun/-/genfun-4.0.1.tgz#ed10041f2e4a7f1b0a38466d17a5c3e27df1dfc1" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + dependencies: + assert-plus "^1.0.0" + +gitbook-asciidoc@1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/gitbook-asciidoc/-/gitbook-asciidoc-1.2.2.tgz#dfae3f7c7e228fdcd75b62d859794c05825a2800" + dependencies: + asciidoctor.js "1.5.5-1" + gitbook-html "1.3.3" + lodash "^4.13.1" + +gitbook-cli@^2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/gitbook-cli/-/gitbook-cli-2.3.2.tgz#5e893582e1f743f6fa920c3c3eb36b62ea4a31a0" + dependencies: + bash-color "0.0.4" + commander "2.11.0" + fs-extra "3.0.1" + lodash "4.17.4" + npm "5.1.0" + npmi "1.0.1" + optimist "0.6.1" + q "1.5.0" + semver "5.3.0" + tmp "0.0.31" + user-home "2.0.0" + +gitbook-html@1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/gitbook-html/-/gitbook-html-1.3.3.tgz#055055940c2ca7cd1d6ea70a34196bf2b85c7fef" + dependencies: + cheerio "^0.20.0 && >=0.20.0" + lodash "^4.13.1" + q "^1.1.2" + +gitbook-markdown@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/gitbook-markdown/-/gitbook-markdown-1.3.2.tgz#c4238165eba74ae8dec7b14daddaf1b8a76be830" + dependencies: + gitbook-html "1.3.3" + kramed "0.5.6" + kramed-text-renderer "0.2.1" + lodash "^4.13.1" + +gitbook-plugin-fontsettings@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/gitbook-plugin-fontsettings/-/gitbook-plugin-fontsettings-2.0.0.tgz#835f900ae3dd111086fe7ed4425ee3de024861ab" + +gitbook-plugin-highlight@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/gitbook-plugin-highlight/-/gitbook-plugin-highlight-2.0.2.tgz#02650720ab594c799b9451785960fe417cad8046" + dependencies: + highlight.js "9.2.0" + +gitbook-plugin-livereload@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/gitbook-plugin-livereload/-/gitbook-plugin-livereload-0.0.1.tgz#c0c9dc6f55f82fc24f97532b25287abbbb416b96" + +gitbook-plugin-lunr@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gitbook-plugin-lunr/-/gitbook-plugin-lunr-1.2.0.tgz#f3e80ec76512fbdb75eb0524aa82b8dff12a50ab" + dependencies: + gitbook-plugin-search "*" + html-entities "1.2.0" + lunr "0.5.12" + +gitbook-plugin-search@*, gitbook-plugin-search@2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/gitbook-plugin-search/-/gitbook-plugin-search-2.2.1.tgz#6d25b5a776990fa98fdfdfa37de331f78e0f6b13" + +gitbook-plugin-sharing@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/gitbook-plugin-sharing/-/gitbook-plugin-sharing-1.0.2.tgz#532b3af96fafba977ad3c047122642f1eeac6e81" + dependencies: + lodash "^3.10.1" + +gitbook-plugin-theme-default@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/gitbook-plugin-theme-default/-/gitbook-plugin-theme-default-1.0.7.tgz#51f71fbca74a261054e9e9d141c42167afefce88" + +gitbook@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/gitbook/-/gitbook-3.2.3.tgz#8c3043df0589e6f328f49c043002de56f3bc2046" + dependencies: + bash-color "0.0.4" + cheerio "0.20.0" + chokidar "1.5.0" + cp "0.2.0" + cpr "1.1.1" + crc "3.4.0" + destroy "1.0.4" + direction "0.1.5" + dom-serializer "0.1.0" + error "7.0.2" + escape-html "^1.0.3" + escape-string-regexp "1.0.5" + extend "^3.0.0" + fresh-require "1.0.3" + front-matter "^2.1.0" + gitbook-asciidoc "1.2.2" + gitbook-markdown "1.3.2" + gitbook-plugin-fontsettings "2.0.0" + gitbook-plugin-highlight "2.0.2" + gitbook-plugin-livereload "0.0.1" + gitbook-plugin-lunr "1.2.0" + gitbook-plugin-search "2.2.1" + gitbook-plugin-sharing "1.0.2" + gitbook-plugin-theme-default "1.0.7" + github-slugid "1.0.1" + graceful-fs "4.1.4" + i18n-t "1.0.1" + ignore "3.1.2" + immutable "^3.8.1" + is "^3.1.0" + js-yaml "^3.6.1" + json-schema-defaults "0.1.1" + jsonschema "1.1.0" + juice "2.0.0" + mkdirp "0.5.1" + moment "2.13.0" + npm "3.9.2" + npmi "2.0.1" + nunjucks "2.5.2" + nunjucks-do "1.0.0" + object-path "^0.9.2" + omit-keys "^0.1.0" + open "0.0.5" + q "1.4.1" + read-installed "^4.0.3" + request "2.72.0" + resolve "1.1.7" + rmdir "1.2.0" + semver "5.1.0" + send "0.13.2" + spawn-cmd "0.0.2" + tiny-lr "0.2.1" + tmp "0.0.28" + urijs "1.18.0" + +github-slugid@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/github-slugid/-/github-slugid-1.0.1.tgz#bccdd0815bfad69d8a359fa4fd65947d606ec3c0" + +github-url-from-git@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/github-url-from-git/-/github-url-from-git-1.4.0.tgz#285e6b520819001bde128674704379e4ff03e0de" + +github-url-from-username-repo@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/github-url-from-username-repo/-/github-url-from-username-repo-1.0.2.tgz#7dd79330d2abe69c10c2cef79714c97215791dfa" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +"glob@3 || 4": + version "4.5.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-4.5.3.tgz#c6cb73d3226c1efef04de3c56d012f03377ee15f" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "^2.0.1" + once "^1.3.0" + +glob@^6.0.1: + version "6.0.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@~7.1.0, glob@~7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@~7.0.3, glob@~7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +got@^6.7.1: + version "6.7.1" + resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" + dependencies: + create-error-class "^3.0.0" + duplexer3 "^0.1.4" + get-stream "^3.0.0" + is-redirect "^1.0.0" + is-retry-allowed "^1.0.0" + is-stream "^1.0.0" + lowercase-keys "^1.0.0" + safe-buffer "^5.0.1" + timed-out "^4.0.0" + unzip-response "^2.0.1" + url-parse-lax "^1.0.0" + +graceful-fs@4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.4.tgz#ef089d2880f033b011823ce5c8fae798da775dbd" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@~4.1.11, graceful-fs@~4.1.2, graceful-fs@~4.1.4, graceful-fs@~4.1.6, graceful-fs@~4.1.9: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +har-validator@~5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" + dependencies: + ajv "^5.1.0" + har-schema "^2.0.0" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-color@^0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-unicode@^2.0.0, has-unicode@~2.0.0, has-unicode@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" + dependencies: + function-bind "^1.0.2" + +hawk@3.1.3, hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hawk@~6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-6.0.2.tgz#af4d914eb065f9b5ce4d9d11c1cb2126eecc3038" + dependencies: + boom "4.x.x" + cryptiles "3.x.x" + hoek "4.x.x" + sntp "2.x.x" + +highlight.js@9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.2.0.tgz#0d4ef61279c4067da55edf8976f1b9f28f69f202" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +hoek@4.x.x: + version "4.2.1" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb" + +hosted-git-info@^2.1.4, hosted-git-info@^2.1.5, hosted-git-info@^2.4.2, hosted-git-info@~2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" + +hosted-git-info@~2.1.4, hosted-git-info@~2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.1.5.tgz#0ba81d90da2e25ab34a332e6ec77936e1598118b" + +html-entities@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2" + +htmlparser2@^3.9.0: + version "3.9.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" + +htmlparser2@~3.8.1: + version "3.8.3" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" + dependencies: + domelementtype "1" + domhandler "2.3" + domutils "1.5" + entities "1.0" + readable-stream "1.1" + +http-cache-semantics@^3.8.0: + version "3.8.1" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" + +http-errors@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.3.1.tgz#197e22cdebd4198585e8694ef6786197b91ed942" + dependencies: + inherits "~2.0.1" + statuses "1" + +http-parser-js@>=0.4.0: + version "0.4.11" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.11.tgz#5b720849c650903c27e521633d94696ee95f3529" + +http-proxy-agent@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + dependencies: + agent-base "4" + debug "3.1.0" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.0.tgz#7fbba856be8cd677986f42ebd3664f6317257887" + dependencies: + agent-base "^4.1.0" + debug "^3.1.0" + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + dependencies: + ms "^2.0.0" + +i18n-t@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/i18n-t/-/i18n-t-1.0.1.tgz#b7bf4b003e395dc747843e8fca5de477658b0b1c" + dependencies: + lodash "^4.13.1" + +iconv-lite@0.4.13: + version "0.4.13" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" + +iconv-lite@~0.4.13: + version "0.4.19" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" + +iferr@^0.1.5, iferr@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + +ignore@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.1.2.tgz#dd17765e9233b4019762ba82b892202b0980161b" + +immutable@^3.8.1: + version "3.8.2" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" + +import-lazy@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +inflight@^1.0.4, inflight@~1.0.4, inflight@~1.0.5, inflight@~1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@^1.3.4, ini@~1.3.0, ini@~1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +init-package-json@~1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-1.10.1.tgz#cd873a167796befb99612b28762a0b6393fd8f6a" + dependencies: + glob "^7.1.1" + npm-package-arg "^4.0.0 || ^5.0.0" + promzard "^0.3.0" + read "~1.0.1" + read-package-json "1 || 2" + semver "2.x || 3.x || 4 || 5" + validate-npm-package-license "^3.0.1" + validate-npm-package-name "^3.0.0" + +init-package-json@~1.9.3, init-package-json@~1.9.4: + version "1.9.6" + resolved "https://registry.yarnpkg.com/init-package-json/-/init-package-json-1.9.6.tgz#789fc2b74466a4952b9ea77c0575bc78ebd60a61" + dependencies: + glob "^7.1.1" + npm-package-arg "^4.0.0 || ^5.0.0" + promzard "^0.3.0" + read "~1.0.1" + read-package-json "1 || 2" + semver "2.x || 3.x || 4 || 5" + validate-npm-package-license "^3.0.1" + validate-npm-package-name "^3.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +ip@^1.1.4: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-callable@^1.1.1, is-callable@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" + +is-date-object@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-my-ip-valid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" + +is-my-json-valid@^2.12.4: + version "2.17.2" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz#6b2103a288e94ef3de5cf15d29dd85fc4b78d65c" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + is-my-ip-valid "^1.0.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-npm@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4" + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-redirect@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24" + +is-regex@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" + dependencies: + has "^1.0.1" + +is-require@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/is-require/-/is-require-0.0.1.tgz#0d1e6d93e380b35386f474543fffc9a66d41825e" + +is-retry-allowed@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" + +is-stream@^1.0.0, is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-symbol@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +is@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is/-/is-3.2.1.tgz#d0ac2ad55eb7b0bec926a5266f6c662aaa83dca5" + +is@~0.2.6: + version "0.2.7" + resolved "https://registry.yarnpkg.com/is/-/is-0.2.7.tgz#3b34a2c48f359972f35042849193ae7264b63562" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-0.2.0.tgz#a3432192f39b910b5f02cc989487836ec70aa85e" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +js-yaml@^3.10.0, js-yaml@^3.6.1: + version "3.11.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +jsdom@^7.0.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-7.2.2.tgz#40b402770c2bda23469096bee91ab675e3b1fc6e" + dependencies: + abab "^1.0.0" + acorn "^2.4.0" + acorn-globals "^1.0.4" + cssom ">= 0.3.0 < 0.4.0" + cssstyle ">= 0.2.29 < 0.3.0" + escodegen "^1.6.1" + nwmatcher ">= 1.3.7 < 2.0.0" + parse5 "^1.5.1" + request "^2.55.0" + sax "^1.1.4" + symbol-tree ">= 3.1.0 < 4.0.0" + tough-cookie "^2.2.0" + webidl-conversions "^2.0.0" + whatwg-url-compat "~0.6.5" + xml-name-validator ">= 2.0.1 < 3.0.0" + +json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.1.tgz#50183cd1b2d25275de069e9e71b467ac9eab973a" + +json-schema-defaults@0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/json-schema-defaults/-/json-schema-defaults-0.1.1.tgz#1c6c6103c4349b71b6f4aa382c3dd9aa1c8bada3" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +jsonfile@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +jsonschema@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.1.0.tgz#039cc219ed524b06e71daf12ca60d02639faf29a" + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +juice@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/juice/-/juice-2.0.0.tgz#72b6c03b163363e39180fa115152642dd6dce971" + dependencies: + batch "0.5.3" + cheerio "0.20.0" + commander "2.9.0" + cross-spawn-async "^2.1.8" + cssom "0.3.1" + deep-extend "^0.4.0" + slick "1.12.2" + web-resource-inliner "2.0.0" + +kind-of@^3.0.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kramed-text-renderer@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/kramed-text-renderer/-/kramed-text-renderer-0.2.1.tgz#abebc07c040faf7942dca64f4eee0c9b145e7951" + +kramed@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/kramed/-/kramed-0.5.6.tgz#5c37979bcbb59cbb7a8d231049409298857b5162" + +latest-version@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" + dependencies: + package-json "^4.0.0" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lazy-property@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lazy-property/-/lazy-property-1.0.0.tgz#84ddc4b370679ba8bd4cdcfa4c06b43d57111147" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +livereload-js@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/livereload-js/-/livereload-js-2.3.0.tgz#c3ab22e8aaf5bf3505d80d098cbad67726548c9a" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +lockfile@~1.0.1, lockfile@~1.0.2, lockfile@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/lockfile/-/lockfile-1.0.3.tgz#2638fc39a0331e9cac1a04b71799931c9c50df79" + +lodash._baseclone@~4.5.0: + version "4.5.7" + resolved "https://registry.yarnpkg.com/lodash._baseclone/-/lodash._baseclone-4.5.7.tgz#ce42ade08384ef5d62fa77c30f61a46e686f8434" + +lodash._basedifference@~4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash._basedifference/-/lodash._basedifference-4.5.0.tgz#56ea7d601367bfa46cd7de115dc3daeb18837938" + dependencies: + lodash._root "~3.0.0" + +lodash._baseflatten@~4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash._baseflatten/-/lodash._baseflatten-4.2.1.tgz#54acad5e6ef53532a5b8269c0ad725470cfd9208" + +lodash._baseuniq@~4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" + dependencies: + lodash._createset "~4.0.0" + lodash._root "~3.0.0" + +lodash._createset@~4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" + +lodash._root@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" + +lodash.clonedeep@~4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.3.2.tgz#d0112c02c76b5223833aebc6a4b6e334f0d057de" + dependencies: + lodash._baseclone "~4.5.0" + +lodash.clonedeep@~4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" + +lodash.isarray@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-4.0.0.tgz#2aca496b28c4ca6d726715313590c02e6ea34403" + +lodash.keys@~4.0.7: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-4.0.8.tgz#c0cf45d2fcf576c83055404d674c7e637c83ae81" + +lodash.pad@^4.1.0: + version "4.5.1" + resolved "https://registry.yarnpkg.com/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70" + +lodash.padend@^4.1.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.padend/-/lodash.padend-4.6.1.tgz#53ccba047d06e158d311f45da625f4e49e6f166e" + +lodash.padstart@^4.1.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/lodash.padstart/-/lodash.padstart-4.6.1.tgz#d2e3eebff0d9d39ad50f5cbd1b52a7bce6bb611b" + +lodash.rest@^4.0.0: + version "4.0.5" + resolved "https://registry.yarnpkg.com/lodash.rest/-/lodash.rest-4.0.5.tgz#954ef75049262038c96d1fc98b28fdaf9f0772aa" + +lodash.union@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.4.0.tgz#22be23b4c84b49d0436e573949ad1d4a48c7fa38" + dependencies: + lodash._baseflatten "~4.2.0" + lodash._baseuniq "~4.6.0" + lodash.rest "^4.0.0" + +lodash.union@~4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" + +lodash.uniq@~4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.3.0.tgz#dcad810876841447d8f3ec662323c86a6d938227" + dependencies: + lodash._baseuniq "~4.6.0" + +lodash.uniq@~4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + +lodash.without@~4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.2.0.tgz#f89ec9a8ee2d7ec14f8a9cad72a3f5ee12c5a4a6" + dependencies: + lodash._basedifference "~4.5.0" + lodash.rest "^4.0.0" + +lodash.without@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.without/-/lodash.without-4.4.0.tgz#3cd4574a00b67bae373a94b748772640507b7aac" + +lodash@4.17.4: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + +lodash@^3.10.1: + version "3.10.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" + +lodash@^4.1.0, lodash@^4.13.1, lodash@^4.14.0: + version "4.17.5" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +lowercase-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + +lru-cache@^4.0.0, lru-cache@^4.0.1, lru-cache@^4.1.1, lru-cache@~4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e" + dependencies: + pseudomap "^1.0.1" + yallist "^2.0.0" + +lru-queue@0.1: + version "0.1.0" + resolved "https://registry.yarnpkg.com/lru-queue/-/lru-queue-0.1.0.tgz#2738bd9f0d3cf4f84490c5736c48699ac632cda3" + dependencies: + es5-ext "~0.10.2" + +lunr@0.5.12: + version "0.5.12" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-0.5.12.tgz#a2f6b7d7801cbe2ccb1696da67f1f7788f89e0c8" + +make-dir@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b" + dependencies: + pify "^3.0.0" + +make-fetch-happen@^2.4.13: + version "2.6.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-2.6.0.tgz#8474aa52198f6b1ae4f3094c04e8370d35ea8a38" + dependencies: + agentkeepalive "^3.3.0" + cacache "^10.0.0" + http-cache-semantics "^3.8.0" + http-proxy-agent "^2.0.0" + https-proxy-agent "^2.1.0" + lru-cache "^4.1.1" + mississippi "^1.2.0" + node-fetch-npm "^2.0.2" + promise-retry "^1.1.1" + socks-proxy-agent "^3.0.1" + ssri "^5.0.0" + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + +memoizee@~0.3.8: + version "0.3.10" + resolved "https://registry.yarnpkg.com/memoizee/-/memoizee-0.3.10.tgz#4eca0d8aed39ec9d017f4c5c2f2f6432f42e5c8f" + dependencies: + d "~0.1.1" + es5-ext "~0.10.11" + es6-weak-map "~0.1.4" + event-emitter "~0.3.4" + lru-queue "0.1" + next-tick "~0.2.2" + timers-ext "0.1" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + +micromatch@^2.1.5: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +mime-db@~1.33.0: + version "1.33.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" + +mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + dependencies: + mime-db "~1.33.0" + +mime@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" + +mimer@*: + version "0.3.2" + resolved "https://registry.yarnpkg.com/mimer/-/mimer-0.3.2.tgz#0b83aabdf48eaacfd2e093ed4c0ed3d38eda8073" + +minimatch@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-1.0.0.tgz#e0dd2120b49e1b724ce8d714c520822a9438576d" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.4, minimatch@~3.0.3: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimatch@^2.0.1: + version "2.0.10" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" + dependencies: + brace-expansion "^1.0.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minimist@~0.0.1: + version "0.0.10" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + +mississippi@^1.2.0, mississippi@^1.3.0, mississippi@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-1.3.1.tgz#2a8bb465e86550ac8b36a7b6f45599171d78671e" + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^1.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mississippi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^2.0.1" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mkdirp@0.5.1, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +moment@2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.13.0.tgz#24162d99521e6d40f99ae6939e806d2139eaac52" + +move-concurrently@^1.0.1, move-concurrently@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +ms@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" + +mute-stream@~0.0.4: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + +nan@^2.3.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.9.2.tgz#f564d75f5f8f36a6d9456cca7a6c4fe488ab7866" + +next-tick@1: + version "1.0.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" + +next-tick@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-0.2.2.tgz#75da4a927ee5887e39065880065b7336413b310d" + +node-fetch-npm@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7" + dependencies: + encoding "^0.1.11" + json-parse-better-errors "^1.0.0" + safe-buffer "^5.1.1" + +node-gyp@~3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.3.1.tgz#80f7b6d7c2f9c0495ba42c518a670c99bdf6e4a0" + dependencies: + fstream "^1.0.0" + glob "3 || 4" + graceful-fs "^4.1.2" + minimatch "1" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2" + osenv "0" + path-array "^1.0.0" + request "2" + rimraf "2" + semver "2.x || 3.x || 4 || 5" + tar "^2.0.0" + which "1" + +node-gyp@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.4.0.tgz#dda558393b3ecbbe24c9e6b8703c71194c63fa36" + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + minimatch "^3.0.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3" + osenv "0" + path-array "^1.0.0" + request "2" + rimraf "2" + semver "2.x || 3.x || 4 || 5" + tar "^2.0.0" + which "1" + +node-gyp@~3.6.0, node-gyp@~3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.6.2.tgz#9bfbe54562286284838e750eac05295853fa1c60" + dependencies: + fstream "^1.0.0" + glob "^7.0.3" + graceful-fs "^4.1.2" + minimatch "^3.0.2" + mkdirp "^0.5.0" + nopt "2 || 3" + npmlog "0 || 1 || 2 || 3 || 4" + osenv "0" + request "2" + rimraf "2" + semver "~5.3.0" + tar "^2.0.0" + which "1" + +node-pre-gyp@^0.6.39: + version "0.6.39" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" + dependencies: + detect-libc "^1.0.2" + hawk "3.1.3" + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +node-uuid@~1.4.7: + version "1.4.8" + resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" + +node.extend@1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/node.extend/-/node.extend-1.0.8.tgz#bab04379f7383f4587990c9df07b6a7f65db772b" + dependencies: + is "~0.2.6" + object-keys "~0.4.0" + +node.flow@1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/node.flow/-/node.flow-1.2.3.tgz#e1c44a82aeca8d78b458a77fb3dc642f2eba2649" + dependencies: + node.extend "1.0.8" + +"nopt@2 || 3", nopt@~3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +nopt@^4.0.1, nopt@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-git-url@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/normalize-git-url/-/normalize-git-url-3.0.2.tgz#8e5f14be0bdaedb73e07200310aa416c27350fc4" + +normalize-package-data@^2.0.0, normalize-package-data@^2.3.2, normalize-package-data@^2.4.0, "normalize-package-data@~1.0.1 || ^2.0.0", normalize-package-data@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@~2.3.5: + version "2.3.8" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.0, normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +npm-cache-filename@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/npm-cache-filename/-/npm-cache-filename-1.0.2.tgz#ded306c5b0bfc870a9e9faf823bc5f283e05ae11" + +npm-install-checks@~1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-1.0.7.tgz#6d91aeda0ac96801f1ed7aadee116a6c0a086a57" + dependencies: + npmlog "0.1 || 1 || 2" + semver "^2.3.0 || 3.x || 4 || 5" + +npm-install-checks@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-3.0.0.tgz#d4aecdfd51a53e3723b7b2f93b2ee28e307bc0d7" + dependencies: + semver "^2.3.0 || 3.x || 4 || 5" + +"npm-package-arg@^3.0.0 || ^4.0.0", npm-package-arg@^4.1.1, npm-package-arg@~4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-4.2.1.tgz#593303fdea85f7c422775f17f9eb7670f680e3ec" + dependencies: + hosted-git-info "^2.1.5" + semver "^5.1.0" + +"npm-package-arg@^3.0.0 || ^4.0.0 || ^5.0.0", "npm-package-arg@^4.0.0 || ^5.0.0", npm-package-arg@^5.1.2, npm-package-arg@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-5.1.2.tgz#fb18d17bb61e60900d6312619919bd753755ab37" + dependencies: + hosted-git-info "^2.4.2" + osenv "^0.1.4" + semver "^5.1.0" + validate-npm-package-name "^3.0.0" + +npm-package-arg@~4.1.0, npm-package-arg@~4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-4.1.1.tgz#86d9dca985b4c5e5d59772dfd5de6919998a495a" + dependencies: + hosted-git-info "^2.1.4" + semver "4 || 5" + +npm-pick-manifest@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-1.0.4.tgz#a5ee6510c1fe7221c0bc0414e70924c14045f7e8" + dependencies: + npm-package-arg "^5.1.2" + semver "^5.3.0" + +npm-registry-client@~7.1.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/npm-registry-client/-/npm-registry-client-7.1.2.tgz#ddf243a2bd149d35172fe680aff40dfa20054bc3" + dependencies: + chownr "^1.0.1" + concat-stream "^1.4.6" + graceful-fs "^4.1.2" + mkdirp "^0.5.0" + normalize-package-data "~1.0.1 || ^2.0.0" + npm-package-arg "^3.0.0 || ^4.0.0" + once "^1.3.0" + request "^2.47.0" + retry "^0.8.0" + rimraf "2" + semver "2 >=2.2.1 || 3.x || 4 || 5" + slide "^1.1.3" + optionalDependencies: + npmlog "~2.0.0 || ~3.1.0" + +npm-registry-client@~7.2.1: + version "7.2.1" + resolved "https://registry.yarnpkg.com/npm-registry-client/-/npm-registry-client-7.2.1.tgz#c792266b088cc313f8525e7e35248626c723db75" + dependencies: + concat-stream "^1.5.2" + graceful-fs "^4.1.6" + normalize-package-data "~1.0.1 || ^2.0.0" + npm-package-arg "^3.0.0 || ^4.0.0" + once "^1.3.3" + request "^2.74.0" + retry "^0.10.0" + semver "2 >=2.2.1 || 3.x || 4 || 5" + slide "^1.1.3" + optionalDependencies: + npmlog "~2.0.0 || ~3.1.0" + +npm-registry-client@~8.4.0: + version "8.4.0" + resolved "https://registry.yarnpkg.com/npm-registry-client/-/npm-registry-client-8.4.0.tgz#d52b901685647fc62a4c03eafecb6ceaa5018d4c" + dependencies: + concat-stream "^1.5.2" + graceful-fs "^4.1.6" + normalize-package-data "~1.0.1 || ^2.0.0" + npm-package-arg "^3.0.0 || ^4.0.0 || ^5.0.0" + once "^1.3.3" + request "^2.74.0" + retry "^0.10.0" + semver "2 >=2.2.1 || 3.x || 4 || 5" + slide "^1.1.3" + ssri "^4.1.2" + optionalDependencies: + npmlog "2 || ^3.1.0 || ^4.0.0" + +npm-run-all@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.2.tgz#90d62d078792d20669139e718621186656cea056" + dependencies: + ansi-styles "^3.2.0" + chalk "^2.1.0" + cross-spawn "^5.1.0" + memorystream "^0.3.1" + minimatch "^3.0.4" + ps-tree "^1.1.0" + read-pkg "^3.0.0" + shell-quote "^1.6.1" + string.prototype.padend "^3.0.0" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +npm-user-validate@~0.1.2, npm-user-validate@~0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-0.1.5.tgz#52465d50c2d20294a57125b996baedbf56c5004b" + +npm-user-validate@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/npm-user-validate/-/npm-user-validate-1.0.0.tgz#8ceca0f5cea04d4e93519ef72d0557a75122e951" + +npm@3.9.2: + version "3.9.2" + resolved "https://registry.yarnpkg.com/npm/-/npm-3.9.2.tgz#ae9b3dab82590a1866b2ba685c4406c4ba634407" + dependencies: + abbrev "~1.0.7" + ansicolors "~0.3.2" + ansistyles "~0.1.3" + aproba "~1.0.1" + archy "~1.0.0" + chownr "~1.0.1" + cmd-shim "~2.0.2" + columnify "~1.5.4" + config-chain "~1.1.10" + dezalgo "~1.0.3" + editor "~1.0.0" + fs-vacuum "~1.2.9" + fs-write-stream-atomic "~1.0.8" + fstream "~1.0.8" + fstream-npm "~1.0.7" + glob "~7.0.3" + graceful-fs "~4.1.4" + has-unicode "~2.0.0" + hosted-git-info "~2.1.4" + iferr "~0.1.5" + inflight "~1.0.4" + inherits "~2.0.1" + ini "~1.3.4" + init-package-json "~1.9.3" + lockfile "~1.0.1" + lodash._baseuniq "~4.6.0" + lodash.clonedeep "~4.3.2" + lodash.isarray "~4.0.0" + lodash.keys "~4.0.7" + lodash.union "~4.4.0" + lodash.uniq "~4.3.0" + lodash.without "~4.2.0" + mkdirp "~0.5.1" + node-gyp "~3.3.1" + nopt "~3.0.6" + normalize-git-url "~3.0.2" + normalize-package-data "~2.3.5" + npm-cache-filename "~1.0.2" + npm-install-checks "~3.0.0" + npm-package-arg "~4.1.1" + npm-registry-client "~7.1.0" + npm-user-validate "~0.1.2" + npmlog "~2.0.3" + once "~1.3.3" + opener "~1.4.1" + osenv "~0.1.3" + path-is-inside "~1.0.1" + read "~1.0.7" + read-cmd-shim "~1.0.1" + read-installed "~4.0.3" + read-package-json "~2.0.4" + read-package-tree "~5.1.2" + readable-stream "~2.1.2" + realize-package-specifier "~3.0.3" + request "~2.72.0" + retry "~0.9.0" + rimraf "~2.5.2" + semver "~5.1.0" + sha "~2.0.1" + slide "~1.1.6" + sorted-object "~2.0.0" + strip-ansi "~3.0.1" + tar "~2.2.1" + text-table "~0.2.0" + uid-number "0.0.6" + umask "~1.1.0" + unique-filename "~1.1.0" + unpipe "~1.0.0" + validate-npm-package-name "~2.2.2" + which "~1.2.8" + wrappy "~1.0.1" + write-file-atomic "~1.1.4" + +npm@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/npm/-/npm-5.1.0.tgz#cf8201e044018e9c89532041c90094541982b2c0" + dependencies: + JSONStream "~1.3.1" + abbrev "~1.1.0" + ansi-regex "~3.0.0" + ansicolors "~0.3.2" + ansistyles "~0.1.3" + aproba "~1.1.2" + archy "~1.0.0" + bluebird "~3.5.0" + cacache "~9.2.9" + call-limit "~1.1.0" + chownr "~1.0.1" + cmd-shim "~2.0.2" + columnify "~1.5.4" + config-chain "~1.1.11" + detect-indent "~5.0.0" + dezalgo "~1.0.3" + editor "~1.0.0" + fs-vacuum "~1.2.10" + fs-write-stream-atomic "~1.0.10" + fstream "~1.0.11" + fstream-npm "~1.2.1" + glob "~7.1.2" + graceful-fs "~4.1.11" + has-unicode "~2.0.1" + hosted-git-info "~2.5.0" + iferr "~0.1.5" + inflight "~1.0.6" + inherits "~2.0.3" + ini "~1.3.4" + init-package-json "~1.10.1" + lazy-property "~1.0.0" + lockfile "~1.0.3" + lodash._baseuniq "~4.6.0" + lodash.clonedeep "~4.5.0" + lodash.union "~4.6.0" + lodash.uniq "~4.5.0" + lodash.without "~4.4.0" + lru-cache "~4.1.1" + mississippi "~1.3.0" + mkdirp "~0.5.1" + move-concurrently "~1.0.1" + node-gyp "~3.6.2" + nopt "~4.0.1" + normalize-package-data "~2.4.0" + npm-cache-filename "~1.0.2" + npm-install-checks "~3.0.0" + npm-package-arg "~5.1.2" + npm-registry-client "~8.4.0" + npm-user-validate "~1.0.0" + npmlog "~4.1.2" + once "~1.4.0" + opener "~1.4.3" + osenv "~0.1.4" + pacote "~2.7.38" + path-is-inside "~1.0.2" + promise-inflight "~1.0.1" + read "~1.0.7" + read-cmd-shim "~1.0.1" + read-installed "~4.0.3" + read-package-json "~2.0.9" + read-package-tree "~5.1.6" + readable-stream "~2.3.2" + request "~2.81.0" + retry "~0.10.1" + rimraf "~2.6.1" + safe-buffer "~5.1.1" + semver "~5.3.0" + sha "~2.0.1" + slide "~1.1.6" + sorted-object "~2.0.1" + sorted-union-stream "~2.1.3" + ssri "~4.1.6" + strip-ansi "~4.0.0" + tar "~2.2.1" + text-table "~0.2.0" + uid-number "0.0.6" + umask "~1.1.0" + unique-filename "~1.1.0" + unpipe "~1.0.0" + update-notifier "~2.2.0" + uuid "~3.1.0" + validate-npm-package-name "~3.0.0" + which "~1.2.14" + worker-farm "~1.3.1" + wrappy "~1.0.2" + write-file-atomic "~2.1.0" + +npm@^2.1.12: + version "2.15.12" + resolved "https://registry.yarnpkg.com/npm/-/npm-2.15.12.tgz#df7c3ed5a277c3f9d4b5d819b05311d10a200ae6" + dependencies: + abbrev "~1.0.9" + ansi "~0.3.1" + ansicolors "~0.3.2" + ansistyles "~0.1.3" + archy "~1.0.0" + async-some "~1.0.2" + block-stream "0.0.9" + char-spinner "~1.0.1" + chmodr "~1.0.2" + chownr "~1.0.1" + cmd-shim "~2.0.2" + columnify "~1.5.4" + config-chain "~1.1.10" + dezalgo "~1.0.3" + editor "~1.0.0" + fs-vacuum "~1.2.9" + fs-write-stream-atomic "~1.0.8" + fstream "~1.0.10" + fstream-npm "~1.1.1" + github-url-from-git "~1.4.0" + github-url-from-username-repo "~1.0.2" + glob "~7.0.6" + graceful-fs "~4.1.6" + hosted-git-info "~2.1.5" + inflight "~1.0.4" + inherits "~2.0.3" + ini "~1.3.4" + init-package-json "~1.9.4" + lockfile "~1.0.1" + lru-cache "~4.0.1" + minimatch "~3.0.3" + mkdirp "~0.5.1" + node-gyp "~3.6.0" + nopt "~3.0.6" + normalize-git-url "~3.0.2" + normalize-package-data "~2.3.5" + npm-cache-filename "~1.0.2" + npm-install-checks "~1.0.7" + npm-package-arg "~4.1.0" + npm-registry-client "~7.2.1" + npm-user-validate "~0.1.5" + npmlog "~2.0.4" + once "~1.4.0" + opener "~1.4.1" + osenv "~0.1.3" + path-is-inside "~1.0.0" + read "~1.0.7" + read-installed "~4.0.3" + read-package-json "~2.0.4" + readable-stream "~2.1.5" + realize-package-specifier "~3.0.1" + request "~2.74.0" + retry "~0.10.0" + rimraf "~2.5.4" + semver "~5.1.0" + sha "~2.0.1" + slide "~1.1.6" + sorted-object "~2.0.0" + spdx-license-ids "~1.2.2" + strip-ansi "~3.0.1" + tar "~2.2.1" + text-table "~0.2.0" + uid-number "0.0.6" + umask "~1.1.0" + validate-npm-package-license "~3.0.1" + validate-npm-package-name "~2.2.2" + which "~1.2.11" + wrappy "~1.0.2" + write-file-atomic "~1.1.4" + +npm@^3: + version "3.10.10" + resolved "https://registry.yarnpkg.com/npm/-/npm-3.10.10.tgz#5b1d577e4c8869d6c8603bc89e9cd1637303e46e" + dependencies: + abbrev "~1.0.9" + ansicolors "~0.3.2" + ansistyles "~0.1.3" + aproba "~1.0.4" + archy "~1.0.0" + asap "~2.0.5" + chownr "~1.0.1" + cmd-shim "~2.0.2" + columnify "~1.5.4" + config-chain "~1.1.11" + dezalgo "~1.0.3" + editor "~1.0.0" + fs-vacuum "~1.2.9" + fs-write-stream-atomic "~1.0.8" + fstream "~1.0.10" + fstream-npm "~1.2.0" + glob "~7.1.0" + graceful-fs "~4.1.9" + has-unicode "~2.0.1" + hosted-git-info "~2.1.5" + iferr "~0.1.5" + inflight "~1.0.5" + inherits "~2.0.3" + ini "~1.3.4" + init-package-json "~1.9.4" + lockfile "~1.0.2" + lodash._baseuniq "~4.6.0" + lodash.clonedeep "~4.5.0" + lodash.union "~4.6.0" + lodash.uniq "~4.5.0" + lodash.without "~4.4.0" + mkdirp "~0.5.1" + node-gyp "~3.4.0" + nopt "~3.0.6" + normalize-git-url "~3.0.2" + normalize-package-data "~2.3.5" + npm-cache-filename "~1.0.2" + npm-install-checks "~3.0.0" + npm-package-arg "~4.2.0" + npm-registry-client "~7.2.1" + npm-user-validate "~0.1.5" + npmlog "~4.0.0" + once "~1.4.0" + opener "~1.4.2" + osenv "~0.1.3" + path-is-inside "~1.0.2" + read "~1.0.7" + read-cmd-shim "~1.0.1" + read-installed "~4.0.3" + read-package-json "~2.0.4" + read-package-tree "~5.1.5" + readable-stream "~2.1.5" + realize-package-specifier "~3.0.3" + request "~2.75.0" + retry "~0.10.0" + rimraf "~2.5.4" + semver "~5.3.0" + sha "~2.0.1" + slide "~1.1.6" + sorted-object "~2.0.1" + strip-ansi "~3.0.1" + tar "~2.2.1" + text-table "~0.2.0" + uid-number "0.0.6" + umask "~1.1.0" + unique-filename "~1.1.0" + unpipe "~1.0.0" + validate-npm-package-name "~2.2.2" + which "~1.2.11" + wrappy "~1.0.2" + write-file-atomic "~1.2.0" + +npmi@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/npmi/-/npmi-1.0.1.tgz#15d769273547545e6809dcf0ce18aed48b0290e2" + dependencies: + npm "^2.1.12" + semver "^4.1.0" + +npmi@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/npmi/-/npmi-2.0.1.tgz#32607657e1bd47ca857ab4e9d98f0a0cff96bcea" + dependencies: + npm "^3" + semver "^4.1.0" + +"npmlog@0 || 1 || 2", "npmlog@0.1 || 1 || 2", npmlog@~2.0.3, npmlog@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-2.0.4.tgz#98b52530f2514ca90d09ec5b22c8846722375692" + dependencies: + ansi "~0.3.1" + are-we-there-yet "~1.1.2" + gauge "~1.2.5" + +"npmlog@0 || 1 || 2 || 3", "npmlog@~2.0.0 || ~3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-3.1.2.tgz#2d46fa874337af9498a2f12bb43d8d0be4a36873" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.6.0" + set-blocking "~2.0.0" + +"npmlog@0 || 1 || 2 || 3 || 4", "npmlog@2 || ^3.1.0 || ^4.0.0", npmlog@^4.0.2, npmlog@~4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +npmlog@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.1" + set-blocking "~2.0.0" + +nth-check@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" + dependencies: + boolbase "~1.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +nunjucks-do@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nunjucks-do/-/nunjucks-do-1.0.0.tgz#506bbd4b42db29ab3dada41b455546b9907ef1df" + +nunjucks@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/nunjucks/-/nunjucks-2.5.2.tgz#ea7d346e785b8a4874666c3cca9e18c577fba22c" + dependencies: + asap "^2.0.3" + chokidar "^1.6.0" + yargs "^3.32.0" + +"nwmatcher@>= 1.3.7 < 2.0.0": + version "1.4.3" + resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.3.tgz#64348e3b3d80f035b40ac11563d278f8b72db89c" + +oauth-sign@~0.8.1, oauth-sign@~0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-keys@^1.0.8: + version "1.0.11" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" + +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + +object-path@^0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.9.2.tgz#0fd9a74fc5fad1ae3968b586bda5c632bd6c05a5" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +omit-keys@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/omit-keys/-/omit-keys-0.1.0.tgz#e14ec5ca09cd4ae07fc39e8ca35939738449bec8" + dependencies: + array-difference "0.0.1" + isobject "^0.2.0" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + dependencies: + ee-first "1.1.1" + +once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0, once@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +once@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20" + dependencies: + wrappy "1" + +opal-npm-wrapper@^0.9.0-beta2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/opal-npm-wrapper/-/opal-npm-wrapper-0.9.2.tgz#99d178579b571384c97627d6184708fbb0887498" + +open@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/open/-/open-0.0.5.tgz#42c3e18ec95466b6bf0dc42f3a2945c3f0cad8fc" + +opener@~1.4.1, opener@~1.4.2, opener@~1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8" + +optimist@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" + dependencies: + lcid "^1.0.0" + +os-tmpdir@^1.0.0, os-tmpdir@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@0, osenv@^0.1.4, osenv@~0.1.3, osenv@~0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +package-json@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed" + dependencies: + got "^6.7.1" + registry-auth-token "^3.0.1" + registry-url "^3.0.3" + semver "^5.1.0" + +pacote@~2.7.38: + version "2.7.38" + resolved "https://registry.yarnpkg.com/pacote/-/pacote-2.7.38.tgz#5091f8774298c26c3eca24606037f1bb73db74c1" + dependencies: + bluebird "^3.5.0" + cacache "^9.2.9" + glob "^7.1.2" + lru-cache "^4.1.1" + make-fetch-happen "^2.4.13" + minimatch "^3.0.4" + mississippi "^1.2.0" + normalize-package-data "^2.4.0" + npm-package-arg "^5.1.2" + npm-pick-manifest "^1.0.4" + osenv "^0.1.4" + promise-inflight "^1.0.1" + promise-retry "^1.1.1" + protoduck "^4.0.0" + safe-buffer "^5.1.1" + semver "^5.3.0" + ssri "^4.1.6" + tar-fs "^1.15.3" + tar-stream "^1.5.4" + unique-filename "^1.1.0" + which "^1.2.12" + +parallel-transform@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + dependencies: + cyclist "~0.2.2" + inherits "^2.0.3" + readable-stream "^2.1.5" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +parse5@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-1.5.1.tgz#9b7f3b0de32be78dc2401b17573ccaf0f6f59d94" + +parseurl@~1.3.0: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + +path-array@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-array/-/path-array-1.0.1.tgz#7e2f0f35f07a2015122b868b7eac0eb2c4fec271" + dependencies: + array-index "^1.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1, path-is-inside@~1.0.0, path-is-inside@~1.0.1, path-is-inside@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-key@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + dependencies: + through "~2.3" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +promise-inflight@^1.0.1, promise-inflight@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + +promise-retry@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-1.1.1.tgz#6739e968e3051da20ce6497fb2b50f6911df3d6d" + dependencies: + err-code "^1.0.0" + retry "^0.10.0" + +promzard@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/promzard/-/promzard-0.3.0.tgz#26a5d6ee8c7dee4cb12208305acfb93ba382a9ee" + dependencies: + read "1" + +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + +protoduck@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-4.0.0.tgz#fe4874d8c7913366cfd9ead12453a22cd3657f8e" + dependencies: + genfun "^4.0.1" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + +ps-tree@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014" + dependencies: + event-stream "~3.3.0" + +pseudomap@^1.0.1, pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +pump@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pump@^2.0.0, pump@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.4.0.tgz#80b7c5df7e24153d03f0e7ac8a05a5d068bd07fb" + dependencies: + duplexify "^3.5.3" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +q@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" + +q@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1" + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + +qs@5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-5.2.0.tgz#a9f31142af468cb72b25b30136ba2456834916be" + +qs@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-5.1.0.tgz#4d932e5c7ea411cca76a312d39a606200fd50cd9" + +qs@~6.1.0: + version "6.1.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.1.2.tgz#b59d8925d0c999ef6d63acf4ac5abb0adaa24b54" + +qs@~6.2.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe" + +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +qs@~6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" + +randomatic@^1.1.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +range-parser@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.0.3.tgz#6872823535c692e2c2a0103826afd82c2e0ff175" + +raw-body@~2.1.5: + version "2.1.7" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774" + dependencies: + bytes "2.4.0" + iconv-lite "0.4.13" + unpipe "1.0.0" + +rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: + version "1.2.5" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.5.tgz#275cd687f6e3b36cc756baa26dfee80a790301fd" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-cmd-shim@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz#2d5d157786a37c055d22077c32c53f8329e91c7b" + dependencies: + graceful-fs "^4.1.2" + +read-installed@^4.0.3, read-installed@~4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/read-installed/-/read-installed-4.0.3.tgz#ff9b8b67f187d1e4c29b9feb31f6b223acd19067" + dependencies: + debuglog "^1.0.1" + read-package-json "^2.0.0" + readdir-scoped-modules "^1.0.0" + semver "2 || 3 || 4 || 5" + slide "~1.1.3" + util-extend "^1.0.1" + optionalDependencies: + graceful-fs "^4.1.2" + +"read-package-json@1 || 2", read-package-json@^2.0.0, read-package-json@~2.0.4, read-package-json@~2.0.9: + version "2.0.12" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.0.12.tgz#68ea45f98b3741cb6e10ae3bbd42a605026a6951" + dependencies: + glob "^7.1.1" + json-parse-better-errors "^1.0.0" + normalize-package-data "^2.0.0" + slash "^1.0.0" + optionalDependencies: + graceful-fs "^4.1.2" + +read-package-tree@~5.1.2, read-package-tree@~5.1.5, read-package-tree@~5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/read-package-tree/-/read-package-tree-5.1.6.tgz#4f03e83d0486856fb60d97c94882841c2a7b1b7a" + dependencies: + debuglog "^1.0.1" + dezalgo "^1.0.0" + once "^1.3.0" + read-package-json "^2.0.0" + readdir-scoped-modules "^1.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +read@1, read@~1.0.1, read@~1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" + dependencies: + mute-stream "~0.0.4" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@~2.3.2: + version "2.3.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.0.3" + util-deprecate "~1.0.1" + +readable-stream@1.1: + version "1.1.13" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +"readable-stream@>=1.0.33-1 <1.1.0-0": + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@~1.1.10: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@~2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readable-stream@~2.1.2, readable-stream@~2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.1.5.tgz#66fa8b720e1438b364681f2ad1a63c618448c9d0" + dependencies: + buffer-shims "^1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~0.10.x" + util-deprecate "~1.0.1" + +readdir-scoped-modules@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747" + dependencies: + debuglog "^1.0.1" + dezalgo "^1.0.0" + graceful-fs "^4.1.2" + once "^1.3.0" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +realize-package-specifier@~3.0.1, realize-package-specifier@~3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/realize-package-specifier/-/realize-package-specifier-3.0.3.tgz#d0def882952b8de3f67eba5e91199661271f41f4" + dependencies: + dezalgo "^1.0.1" + npm-package-arg "^4.1.1" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + dependencies: + is-equal-shallow "^0.1.3" + +registry-auth-token@^3.0.1: + version "3.3.2" + resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20" + dependencies: + rc "^1.1.6" + safe-buffer "^5.0.1" + +registry-url@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942" + dependencies: + rc "^1.0.1" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +request@2, request@^2.47.0, request@^2.49.0, request@^2.55.0, request@^2.74.0: + version "2.83.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + +request@2.72.0, request@~2.72.0: + version "2.72.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.72.0.tgz#0ce3a179512620b10441f14c82e21c12c0ddb4e1" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + bl "~1.1.2" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~1.0.0-rc3" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + node-uuid "~1.4.7" + oauth-sign "~0.8.1" + qs "~6.1.0" + stringstream "~0.0.4" + tough-cookie "~2.2.0" + tunnel-agent "~0.4.1" + +request@2.81.0, request@~2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +request@~2.74.0: + version "2.74.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.74.0.tgz#7693ca768bbb0ea5c8ce08c084a45efa05b892ab" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + bl "~1.1.2" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~1.0.0-rc4" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + node-uuid "~1.4.7" + oauth-sign "~0.8.1" + qs "~6.2.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + +request@~2.75.0: + version "2.75.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.75.0.tgz#d2b8268a286da13eaa5d01adf5d18cc90f657d93" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + bl "~1.1.2" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.0.0" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + node-uuid "~1.4.7" + oauth-sign "~0.8.1" + qs "~6.2.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + +resolve@1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + +resolve@^1.0.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" + dependencies: + path-parse "^1.0.5" + +retry@^0.10.0, retry@~0.10.0, retry@~0.10.1: + version "0.10.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" + +retry@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.8.0.tgz#2367628dc0edb247b1eab649dc53ac8628ac2d5f" + +retry@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.9.0.tgz#6f697e50a0e4ddc8c8f7fb547a9b60dead43678d" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@2, rimraf@^2.5.1, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@~2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +rimraf@~2.4.3: + version "2.4.5" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da" + dependencies: + glob "^6.0.1" + +rimraf@~2.5.2, rimraf@~2.5.4: + version "2.5.4" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04" + dependencies: + glob "^7.0.5" + +rmdir@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/rmdir/-/rmdir-1.2.0.tgz#4fe0357cb06168c258e73e968093dc4e8a0f3253" + dependencies: + node.flow "1.2.3" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + dependencies: + aproba "^1.1.1" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" + +sax@^1.1.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +semver-diff@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" + dependencies: + semver "^5.0.3" + +"semver@2 >=2.2.1 || 3.x || 4 || 5", "semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@4 || 5", "semver@^2.3.0 || 3.x || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + +semver@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.0.tgz#85f2cf8550465c4df000cf7d86f6b054106ab9e5" + +semver@5.3.0, semver@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +semver@^4.1.0: + version "4.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" + +semver@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.1.1.tgz#a3292a373e6f3e0798da0b20641b9a9c5bc47e19" + +send@0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.13.2.tgz#765e7607c8055452bba6f0b052595350986036de" + dependencies: + debug "~2.2.0" + depd "~1.1.0" + destroy "~1.0.4" + escape-html "~1.0.3" + etag "~1.7.0" + fresh "0.3.0" + http-errors "~1.3.1" + mime "1.3.4" + ms "0.7.1" + on-finished "~2.3.0" + range-parser "~1.0.3" + statuses "~1.2.1" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +sha@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/sha/-/sha-2.0.1.tgz#6030822fbd2c9823949f8f72ed6411ee5cf25aae" + dependencies: + graceful-fs "^4.1.2" + readable-stream "^2.0.2" + +shallow-copy@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/shallow-copy/-/shallow-copy-0.0.1.tgz#415f42702d73d810330292cc5ee86eae1a11a170" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +shell-quote@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +sleuth@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/sleuth/-/sleuth-0.1.1.tgz#406efb86730ba5c27147b570186d72c83b0d8cc0" + dependencies: + is-require "0.0.1" + static-eval "~0.1.0" + +slick@1.12.2: + version "1.12.2" + resolved "https://registry.yarnpkg.com/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7" + +slide@^1.1.3, slide@^1.1.5, slide@~1.1.3, slide@~1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" + +smart-buffer@^1.0.13: + version "1.1.15" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +sntp@2.x.x: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-2.1.0.tgz#2c6cec14fedc2222739caf9b5c3d85d1cc5a2cc8" + dependencies: + hoek "4.x.x" + +socks-proxy-agent@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz#2eae7cf8e2a82d34565761539a7f9718c5617659" + dependencies: + agent-base "^4.1.0" + socks "^1.1.10" + +socks@^1.1.10: + version "1.1.10" + resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a" + dependencies: + ip "^1.1.4" + smart-buffer "^1.0.13" + +sorted-object@~2.0.0, sorted-object@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/sorted-object/-/sorted-object-2.0.1.tgz#7d631f4bd3a798a24af1dffcfbfe83337a5df5fc" + +sorted-union-stream@~2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/sorted-union-stream/-/sorted-union-stream-2.1.3.tgz#c7794c7e077880052ff71a8d4a2dbb4a9a638ac7" + dependencies: + from2 "^1.3.0" + stream-iterate "^1.1.0" + +source-map@~0.5.1: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +spawn-cmd@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/spawn-cmd/-/spawn-cmd-0.0.2.tgz#6d5e251fad0eab00b0f193d245669a7a228ec0de" + +spdx-correct@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" + +spdx-license-ids@~1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" + +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + dependencies: + through "2" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +ssri@^4.1.2, ssri@^4.1.6, ssri@~4.1.6: + version "4.1.6" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-4.1.6.tgz#0cb49b6ac84457e7bdd466cb730c3cb623e9a25b" + dependencies: + safe-buffer "^5.1.0" + +ssri@^5.0.0, ssri@^5.2.4: + version "5.2.4" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.2.4.tgz#9985e14041e65fc397af96542be35724ac11da52" + dependencies: + safe-buffer "^5.1.1" + +static-eval@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-0.1.1.tgz#2f3c9e727604a61ac761b9663562a76c61f5c523" + +statuses@1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + +statuses@~1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.2.1.tgz#dded45cc18256d51ed40aec142489d5c61026d28" + +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + dependencies: + duplexer "~0.1.1" + +stream-each@^1.1.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd" + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-iterate@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/stream-iterate/-/stream-iterate-1.2.0.tgz#2bd7c77296c1702a46488b8ad41f79865eecd4e1" + dependencies: + readable-stream "^2.1.5" + stream-shift "^1.0.0" + +stream-shift@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + +string-template@~0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" + +string-width@^1.0.1, string-width@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string-width@^2.0.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string.prototype.padend@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz#f3aaef7c1719f170c5eab1c32bf780d96e21f2f0" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.4.3" + function-bind "^1.0.2" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +string_decoder@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" + dependencies: + safe-buffer "~5.1.0" + +stringstream@~0.0.4, stringstream@~0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1, strip-ansi@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0, strip-ansi@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" + dependencies: + has-flag "^3.0.0" + +"symbol-tree@>= 3.1.0 < 4.0.0": + version "3.2.2" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" + +tar-fs@^1.15.3: + version "1.16.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.0.tgz#e877a25acbcc51d8c790da1c57c9cf439817b896" + dependencies: + chownr "^1.0.1" + mkdirp "^0.5.1" + pump "^1.0.0" + tar-stream "^1.1.2" + +tar-pack@^3.4.0: + version "3.4.1" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar-stream@^1.1.2, tar-stream@^1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.5.5.tgz#5cad84779f45c83b1f2508d96b09d88c7218af55" + dependencies: + bl "^1.0.0" + end-of-stream "^1.0.0" + readable-stream "^2.0.0" + xtend "^4.0.0" + +tar@^2.0.0, tar@^2.2.1, tar@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +templayed@*: + version "0.2.3" + resolved "https://registry.yarnpkg.com/templayed/-/templayed-0.2.3.tgz#4706df625bc6aecd86b7c9f6b0fb548b95cdf769" + +term-size@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" + dependencies: + execa "^0.7.0" + +text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through2@^0.6.3: + version "0.6.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.6.5.tgz#41ab9c67b29d57209071410e1d7a7a968cd3ad48" + dependencies: + readable-stream ">=1.0.33-1 <1.1.0-0" + xtend ">=4.0.0 <4.1.0-0" + +through2@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" + dependencies: + readable-stream "^2.1.5" + xtend "~4.0.1" + +through@2, "through@>=2.2.7 <3", through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +timed-out@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" + +timers-ext@0.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/timers-ext/-/timers-ext-0.1.3.tgz#cf3ae91ba16990401a74ce85b00efb8f605e6fdc" + dependencies: + es5-ext "~0.10.14" + next-tick "1" + +tiny-lr@0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tiny-lr/-/tiny-lr-0.2.1.tgz#b3fdba802e5d56a33c2f6f10794b32e477ac729d" + dependencies: + body-parser "~1.14.0" + debug "~2.2.0" + faye-websocket "~0.10.0" + livereload-js "^2.2.0" + parseurl "~1.3.0" + qs "~5.1.0" + +tmp@0.0.28: + version "0.0.28" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120" + dependencies: + os-tmpdir "~1.0.1" + +tmp@0.0.31: + version "0.0.31" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" + dependencies: + os-tmpdir "~1.0.1" + +tough-cookie@^2.2.0, tough-cookie@~2.3.0, tough-cookie@~2.3.3: + version "2.3.4" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" + dependencies: + punycode "^1.4.1" + +tough-cookie@~2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.2.2.tgz#c83a1830f4e5ef0b93ef2a3488e724f8de016ac7" + +tr46@~0.0.1: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +type-is@~1.6.10: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.18" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +uglify-js@^2.4.1: + version "2.8.29" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +uid-number@0.0.6, uid-number@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +umask@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" + +unique-filename@^1.1.0, unique-filename@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3" + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab" + dependencies: + imurmurhash "^0.1.4" + +unique-string@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" + dependencies: + crypto-random-string "^1.0.0" + +universalify@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + +unzip-response@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97" + +update-notifier@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.2.0.tgz#1b5837cf90c0736d88627732b661c138f86de72f" + dependencies: + boxen "^1.0.0" + chalk "^1.0.0" + configstore "^3.0.0" + import-lazy "^2.1.0" + is-npm "^1.0.0" + latest-version "^3.0.0" + semver-diff "^2.0.0" + xdg-basedir "^3.0.0" + +urijs@1.18.0: + version "1.18.0" + resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.18.0.tgz#091cbb7f6d60401b2e5e35c57a04177b9bbd12f2" + +url-parse-lax@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" + dependencies: + prepend-http "^1.0.1" + +user-home@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" + dependencies: + os-homedir "^1.0.0" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util-extend@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/util-extend/-/util-extend-1.0.3.tgz#a7c216d267545169637b3b6edc6ca9119e2ff93f" + +uuid@^3.0.0, uuid@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" + +uuid@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" + +validate-npm-package-license@^3.0.1, validate-npm-package-license@~3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +validate-npm-package-name@^3.0.0, validate-npm-package-name@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" + dependencies: + builtins "^1.0.3" + +validate-npm-package-name@~2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-2.2.2.tgz#f65695b22f7324442019a3c7fa39a6e7fd299085" + dependencies: + builtins "0.0.7" + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +wcwidth@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + dependencies: + defaults "^1.0.3" + +web-resource-inliner@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/web-resource-inliner/-/web-resource-inliner-2.0.0.tgz#abf934fc86be665b80a56ecf2f04f2ee14e6746c" + dependencies: + async "^0.9.0" + clean-css "1.1.7" + cli-color "^0.3.2" + datauri "~0.2.0" + htmlparser2 "^3.9.0" + lodash "^3.10.1" + request "^2.49.0" + uglify-js "^2.4.1" + xtend "^4.0.0" + +webidl-conversions@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506" + +websocket-driver@>=0.5.1: + version "0.7.0" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" + dependencies: + http-parser-js ">=0.4.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" + +whatwg-url-compat@~0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz#00898111af689bb097541cd5a45ca6c8798445bf" + dependencies: + tr46 "~0.0.1" + +which@1, which@^1.2.12, which@^1.2.8, which@^1.2.9: + version "1.3.0" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" + dependencies: + isexe "^2.0.0" + +which@~1.2.11, which@~1.2.14, which@~1.2.8: + version "1.2.14" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" + dependencies: + string-width "^1.0.2" + +widest-line@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273" + dependencies: + string-width "^2.1.1" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +window-size@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +worker-farm@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.3.1.tgz#4333112bb49b17aa050b87895ca6b2cacf40e5ff" + dependencies: + errno ">=0.1.1 <0.2.0-0" + xtend ">=4.0.0 <4.1.0-0" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1, wrappy@~1.0.1, wrappy@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write-file-atomic@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab" + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + signal-exit "^3.0.2" + +write-file-atomic@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.1.4.tgz#b1f52dc2e8dc0e3cb04d187a25f758a38a90ca3b" + dependencies: + graceful-fs "^4.1.2" + imurmurhash "^0.1.4" + slide "^1.1.5" + +write-file-atomic@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.2.0.tgz#14c66d4e4cb3ca0565c28cf3b7a6f3e4d5938fab" + dependencies: + graceful-fs "^4.1.2" + imurmurhash "^0.1.4" + slide "^1.1.5" + +write-file-atomic@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.1.0.tgz#1769f4b551eedce419f0505deae2e26763542d37" + dependencies: + graceful-fs "^4.1.11" + imurmurhash "^0.1.4" + slide "^1.1.5" + +xdg-basedir@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" + +"xml-name-validator@>= 2.0.1 < 3.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" + +xmlhttprequest@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.7.0.tgz#dc697a8df0258afacad526c1c296b1bdd12c4ab3" + +"xtend@>=4.0.0 <4.1.0-0", xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +y18n@^3.2.0, y18n@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" + +y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + +yallist@^2.0.0, yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yargs@^3.32.0: + version "3.32.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" + dependencies: + camelcase "^2.0.1" + cliui "^3.0.3" + decamelize "^1.1.1" + os-locale "^1.4.0" + string-width "^1.0.1" + window-size "^0.1.4" + y18n "^3.2.0" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" diff --git a/apis/hydra.json b/apis/hydra.json index b4e404b30..82a2a374d 100644 --- a/apis/hydra.json +++ b/apis/hydra.json @@ -12,24 +12,15 @@ ], "swagger": "2.0", "info": { - "description": "Welcome to the ORY Hydra HTTP API documentation. You will find documentation for all HTTP APIs here. Keep in mind that this document reflects the latest branch, always. Support for versioned documentation is coming in the future.", - "title": "ORY Hydra - Cloud Native OAuth 2.0 and OpenID Connect Server", - "contact": { - "name": "ORY", - "url": "https://www.ory.sh", - "email": "hi@ory.am" - }, - "license": { - "name": "Apache 2.0", - "url": "https://github.com/ory/hydra/blob/master/LICENSE" - }, - "version": "Latest" + "description": "Welcome to the ORY Hydra HTTP API documentation. You will find documentation for all HTTP APIs here.", + "title": "ORY Hydra", + "version": "latest" }, "basePath": "/", "paths": { "/.well-known/jwks.json": { "get": { - "description": "Returns metadata for discovering important JSON Web Keys. Currently, this endpoint returns the public key for verifying OpenID Connect ID Tokens.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "description": "This endpoint returns JSON Web Keys to be used as public keys for verifying OpenID Connect ID Tokens and,\nif enabled, OAuth 2.0 JWT Access Tokens. This endpoint can be used with client libraries like\n[node-jwks-rsa](https://github.com/auth0/node-jwks-rsa) among others.", "consumes": [ "application/json" ], @@ -41,9 +32,9 @@ "https" ], "tags": [ - "oAuth2" + "public" ], - "summary": "Get Well-Known JSON Web Keys", + "summary": "JSON Web Keys Discovery", "operationId": "wellKnown", "responses": { "200": { @@ -52,14 +43,11 @@ "$ref": "#/definitions/JSONWebKeySet" } }, - "401": { - "$ref": "#/responses/genericError" - }, - "403": { - "$ref": "#/responses/genericError" - }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -75,10 +63,10 @@ "https" ], "tags": [ - "oAuth2" + "public" ], - "summary": "Server well known configuration", - "operationId": "getWellKnown", + "summary": "OpenID Connect Discovery", + "operationId": "discoverOpenIDConfiguration", "responses": { "200": { "description": "wellKnown", @@ -87,10 +75,16 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -109,7 +103,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "List OAuth 2.0 Clients", "operationId": "listOAuth2Clients", @@ -136,13 +130,22 @@ "$ref": "#/responses/oAuth2ClientList" }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } }, @@ -159,7 +162,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Create an OAuth 2.0 client", "operationId": "createOAuth2Client", @@ -181,13 +184,22 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -206,7 +218,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Get an OAuth 2.0 Client.", "operationId": "getOAuth2Client", @@ -229,13 +241,22 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } }, @@ -252,7 +273,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Update an OAuth 2.0 Client", "operationId": "updateOAuth2Client", @@ -281,13 +302,22 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } }, @@ -304,7 +334,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Deletes an OAuth 2.0 Client", "operationId": "deleteOAuth2Client", @@ -324,13 +354,22 @@ "$ref": "#/responses/emptyResponse" }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -354,7 +393,10 @@ } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -400,7 +442,7 @@ "https" ], "tags": [ - "jsonWebKey" + "admin" ], "summary": "Retrieve a JSON Web Key Set", "operationId": "getJsonWebKeySet", @@ -422,13 +464,22 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } }, @@ -445,7 +496,7 @@ "https" ], "tags": [ - "jsonWebKey" + "admin" ], "summary": "Update a JSON Web Key Set", "operationId": "updateJsonWebKeySet", @@ -474,13 +525,22 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } }, @@ -497,7 +557,7 @@ "https" ], "tags": [ - "jsonWebKey" + "admin" ], "summary": "Generate a new JSON Web Key", "operationId": "createJsonWebKeySet", @@ -526,13 +586,22 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } }, @@ -549,7 +618,7 @@ "https" ], "tags": [ - "jsonWebKey" + "admin" ], "summary": "Delete a JSON Web Key Set", "operationId": "deleteJsonWebKeySet", @@ -568,20 +637,29 @@ "$ref": "#/responses/emptyResponse" }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } }, "/keys/{set}/{kid}": { "get": { - "description": "This endpoint can be used to retrieve JWKs stored in ORY Hydra.\n\nA JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well.", + "description": "This endpoint returns a singular JSON Web Key, identified by the set and the specific key ID (kid).", "consumes": [ "application/json" ], @@ -593,9 +671,9 @@ "https" ], "tags": [ - "jsonWebKey" + "admin" ], - "summary": "Retrieve a JSON Web Key", + "summary": "Fetch a JSON Web Key", "operationId": "getJsonWebKey", "parameters": [ { @@ -622,14 +700,17 @@ "$ref": "#/definitions/JSONWebKeySet" } }, - "401": { - "$ref": "#/responses/genericError" - }, - "403": { - "$ref": "#/responses/genericError" + "404": { + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } }, @@ -646,7 +727,7 @@ "https" ], "tags": [ - "jsonWebKey" + "admin" ], "summary": "Update a JSON Web Key", "operationId": "updateJsonWebKey", @@ -683,13 +764,22 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } }, @@ -706,7 +796,7 @@ "https" ], "tags": [ - "jsonWebKey" + "admin" ], "summary": "Delete a JSON Web Key", "operationId": "deleteJsonWebKey", @@ -733,13 +823,22 @@ "$ref": "#/responses/emptyResponse" }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -755,7 +854,7 @@ "https" ], "tags": [ - "oAuth2" + "public" ], "summary": "The OAuth 2.0 authorize endpoint", "operationId": "oauthAuth", @@ -764,10 +863,16 @@ "$ref": "#/responses/emptyResponse" }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -786,7 +891,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Get consent request information", "operationId": "getConsentRequest", @@ -807,13 +912,22 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "409": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -832,7 +946,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Accept an consent request", "operationId": "acceptConsentRequest", @@ -860,10 +974,16 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -882,7 +1002,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Reject an consent request", "operationId": "rejectConsentRequest", @@ -910,10 +1030,16 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -932,7 +1058,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Get an login request", "operationId": "getLoginRequest", @@ -953,13 +1079,22 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "409": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -978,7 +1113,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Accept an login request", "operationId": "acceptLoginRequest", @@ -1006,10 +1141,16 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1028,7 +1169,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Reject a login request", "operationId": "rejectLoginRequest", @@ -1056,10 +1197,16 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1078,7 +1225,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Lists all consent sessions of a user", "operationId": "listUserConsentSessions", @@ -1096,13 +1243,22 @@ "$ref": "#/responses/handledConsentRequestList" }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "403": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } }, @@ -1119,7 +1275,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Revokes all previous consent sessions of a user", "operationId": "revokeAllUserConsentSessions", @@ -1137,10 +1293,16 @@ "$ref": "#/responses/emptyResponse" }, "404": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1159,7 +1321,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Revokes consent sessions of a user for a specific OAuth 2.0 Client", "operationId": "revokeUserClientConsentSessions", @@ -1184,10 +1346,16 @@ "$ref": "#/responses/emptyResponse" }, "404": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1203,7 +1371,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Logs user out by deleting the session cookie", "operationId": "revokeUserLoginCookie", @@ -1212,10 +1380,16 @@ "$ref": "#/responses/emptyResponse" }, "404": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1234,7 +1408,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Invalidates a user's authentication session", "operationId": "revokeAuthenticationSession", @@ -1252,10 +1426,16 @@ "$ref": "#/responses/emptyResponse" }, "404": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1271,7 +1451,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Flush Expired OAuth2 Access Tokens", "operationId": "flushInactiveOAuth2Tokens", @@ -1289,10 +1469,16 @@ "$ref": "#/responses/emptyResponse" }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1319,7 +1505,7 @@ "https" ], "tags": [ - "oAuth2" + "admin" ], "summary": "Introspect OAuth2 tokens", "operationId": "introspectOAuth2Token", @@ -1348,10 +1534,16 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1375,7 +1567,7 @@ "https" ], "tags": [ - "oAuth2" + "public" ], "summary": "Revoke OAuth2 tokens", "operationId": "revokeOAuth2Token", @@ -1393,10 +1585,16 @@ "$ref": "#/responses/emptyResponse" }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1423,7 +1621,7 @@ "https" ], "tags": [ - "oAuth2" + "public" ], "summary": "The OAuth 2.0 token endpoint", "operationId": "oauthToken", @@ -1435,10 +1633,16 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1459,7 +1663,7 @@ "https" ], "tags": [ - "oAuth2" + "public" ], "summary": "OpenID Connect Userinfo", "operationId": "userinfo", @@ -1471,10 +1675,16 @@ } }, "401": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } }, "500": { - "$ref": "#/responses/genericError" + "description": "genericError", + "schema": { + "$ref": "#/definitions/genericError" + } } } } @@ -1520,70 +1730,91 @@ }, "JSONWebKey": { "type": "object", + "required": [ + "use", + "kty", + "kid", + "alg" + ], "properties": { "alg": { "description": "The \"alg\" (algorithm) parameter identifies the algorithm intended for\nuse with the key. The values used should either be registered in the\nIANA \"JSON Web Signature and Encryption Algorithms\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name.", "type": "string", - "x-go-name": "Alg" + "x-go-name": "Alg", + "example": "RS256" }, "crv": { "type": "string", - "x-go-name": "Crv" + "x-go-name": "Crv", + "example": "P-256" }, "d": { "type": "string", - "x-go-name": "D" + "x-go-name": "D", + "example": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE" }, "dp": { "type": "string", - "x-go-name": "Dp" + "x-go-name": "Dp", + "example": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0" }, "dq": { "type": "string", - "x-go-name": "Dq" + "x-go-name": "Dq", + "example": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk" }, "e": { "type": "string", - "x-go-name": "E" + "x-go-name": "E", + "example": "AQAB" }, "k": { "type": "string", - "x-go-name": "K" + "x-go-name": "K", + "example": "GawgguFyGrWKav7AX4VKUg" }, "kid": { "description": "The \"kid\" (key ID) parameter is used to match a specific key. This\nis used, for instance, to choose among a set of keys within a JWK Set\nduring key rollover. The structure of the \"kid\" value is\nunspecified. When \"kid\" values are used within a JWK Set, different\nkeys within the JWK Set SHOULD use distinct \"kid\" values. (One\nexample in which different keys might use the same \"kid\" value is if\nthey have different \"kty\" (key type) values but are considered to be\nequivalent alternatives by the application using them.) The \"kid\"\nvalue is a case-sensitive string.", "type": "string", - "x-go-name": "Kid" + "x-go-name": "Kid", + "example": "1603dfe0af8f4596" }, "kty": { "description": "The \"kty\" (key type) parameter identifies the cryptographic algorithm\nfamily used with the key, such as \"RSA\" or \"EC\". \"kty\" values should\neither be registered in the IANA \"JSON Web Key Types\" registry\nestablished by [JWA] or be a value that contains a Collision-\nResistant Name. The \"kty\" value is a case-sensitive string.", "type": "string", - "x-go-name": "Kty" + "x-go-name": "Kty", + "example": "RSA" }, "n": { "type": "string", - "x-go-name": "N" + "x-go-name": "N", + "example": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0" }, "p": { "type": "string", - "x-go-name": "P" + "x-go-name": "P", + "example": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ" }, "q": { "type": "string", - "x-go-name": "Q" + "x-go-name": "Q", + "example": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ" }, "qi": { "type": "string", - "x-go-name": "Qi" + "x-go-name": "Qi", + "example": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU" }, "use": { - "description": "The \"use\" (public key use) parameter identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Values are commonly \"sig\" (signature) or \"enc\" (encryption).", + "description": "Use (\"public key use\") identifies the intended use of\nthe public key. The \"use\" parameter is employed to indicate whether\na public key is used for encrypting data or verifying the signature\non data. Values are commonly \"sig\" (signature) or \"enc\" (encryption).", "type": "string", - "x-go-name": "Use" + "x-go-name": "Use", + "example": "sig" }, "x": { "type": "string", - "x-go-name": "X" + "x-go-name": "X", + "example": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU" }, "x5c": { "description": "The \"x5c\" (X.509 certificate chain) parameter contains a chain of one\nor more PKIX certificates [RFC5280]. The certificate chain is\nrepresented as a JSON array of certificate value strings. Each\nstring in the array is a base64-encoded (Section 4 of [RFC4648] --\nnot base64url-encoded) DER [ITU.X690.1994] PKIX certificate value.\nThe PKIX certificate containing the key value MUST be the first\ncertificate.", @@ -1595,7 +1826,8 @@ }, "y": { "type": "string", - "x-go-name": "Y" + "x-go-name": "Y", + "example": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" } }, "x-go-name": "swaggerJSONWebKey", @@ -1656,16 +1888,6 @@ }, "x-go-package": "github.com/ory/hydra/consent" }, - "RawMessage": { - "description": "It implements Marshaler and Unmarshaler and can\nbe used to delay JSON decoding or precompute a JSON encoding.", - "type": "array", - "title": "RawMessage is a raw encoded JSON value.", - "items": { - "type": "integer", - "format": "uint8" - }, - "x-go-package": "encoding/json" - }, "acceptConsentRequest": { "type": "object", "title": "The request payload used to accept a consent request.", @@ -1755,6 +1977,11 @@ "type": "object", "title": "Contains information on an ongoing consent request.", "properties": { + "acr": { + "description": "ACR represents the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it\nto express that, for example, a user authenticated using two factor authentication.", + "type": "string", + "x-go-name": "ACR" + }, "challenge": { "description": "Challenge is the identifier (\"authorization challenge\") of the consent authorization request. It is used to\nidentify the session.", "type": "string", @@ -1835,6 +2062,11 @@ "x-go-name": "ConsentRequestSessionData", "x-go-package": "github.com/ory/hydra/consent" }, + "emptyResponse": { + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\ntypically 201.", + "type": "object", + "x-go-package": "github.com/ory/hydra" + }, "flushInactiveOAuth2TokensRequest": { "type": "object", "properties": { @@ -1848,6 +2080,42 @@ "x-go-name": "FlushInactiveOAuth2TokensRequest", "x-go-package": "github.com/ory/hydra/oauth2" }, + "genericError": { + "description": "Error responses are sent when an error (e.g. unauthorized, bad request, ...) occurred.", + "type": "object", + "title": "Error response", + "required": [ + "error" + ], + "properties": { + "error": { + "description": "Name is the error name.", + "type": "string", + "x-go-name": "Name", + "example": "The requested resource could not be found" + }, + "error_code": { + "description": "Code represents the error status code (404, 403, 401, ...).", + "type": "integer", + "format": "int64", + "x-go-name": "Code", + "example": 404 + }, + "error_debug": { + "description": "Debug contains debug information. This is usually not available and has to be enabled.", + "type": "string", + "x-go-name": "Debug", + "example": "The database adapter was unable to find the element" + }, + "error_hint": { + "description": "Hint contains further information on the nature of the error.", + "type": "string", + "x-go-name": "Hint", + "example": "Object with ID 12345 does not exist" + } + }, + "x-go-package": "github.com/ory/hydra" + }, "healthNotReadyStatus": { "type": "object", "properties": { @@ -1875,19 +2143,6 @@ "x-go-name": "swaggerHealthStatus", "x-go-package": "github.com/ory/x/healthx" }, - "joseWebKeySetRequest": { - "type": "object", - "properties": { - "keys": { - "type": "array", - "items": { - "$ref": "#/definitions/RawMessage" - }, - "x-go-name": "Keys" - } - }, - "x-go-package": "github.com/ory/hydra/jwk" - }, "jsonWebKeySetGeneratorRequest": { "type": "object", "required": [ @@ -2569,7 +2824,9 @@ "x-go-package": "github.com/ory/x/healthx" }, "wellKnown": { + "description": "It includes links to several endpoints (e.g. /oauth2/token) and exposes information on supported signature algorithms\namong others.", "type": "object", + "title": "WellKnown represents important OpenID Connect discovery metadata", "required": [ "issuer", "authorization_endpoint", @@ -2583,7 +2840,8 @@ "authorization_endpoint": { "description": "URL of the OP's OAuth 2.0 Authorization Endpoint.", "type": "string", - "x-go-name": "AuthURL" + "x-go-name": "AuthURL", + "example": "https://playground.ory.sh/ory-hydra/public/oauth2/auth" }, "claims_parameter_supported": { "description": "Boolean value specifying whether the OP supports use of the claims parameter, with true indicating support.", @@ -2617,17 +2875,20 @@ "issuer": { "description": "URL using the https scheme with no query or fragment component that the OP asserts as its IssuerURL Identifier.\nIf IssuerURL discovery is supported , this value MUST be identical to the issuer value returned\nby WebFinger. This also MUST be identical to the iss Claim value in ID Tokens issued from this IssuerURL.", "type": "string", - "x-go-name": "Issuer" + "x-go-name": "Issuer", + "example": "https://playground.ory.sh/ory-hydra/public/" }, "jwks_uri": { "description": "URL of the OP's JSON Web Key Set [JWK] document. This contains the signing key(s) the RP uses to validate\nsignatures from the OP. The JWK Set MAY also contain the Server's encryption key(s), which are used by RPs\nto encrypt requests to the Server. When both signing and encryption keys are made available, a use (Key Use)\nparameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage.\nAlthough some algorithms allow the same key to be used for both signatures and encryption, doing so is\nNOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of\nkeys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.", "type": "string", - "x-go-name": "JWKsURI" + "x-go-name": "JWKsURI", + "example": "https://playground.ory.sh/ory-hydra/public/.well-known/jwks.json" }, "registration_endpoint": { "description": "URL of the OP's Dynamic Client Registration Endpoint.", "type": "string", - "x-go-name": "RegistrationEndpoint" + "x-go-name": "RegistrationEndpoint", + "example": "https://playground.ory.sh/ory-hydra/admin/client" }, "request_parameter_supported": { "description": "Boolean value specifying whether the OP supports use of the request parameter, with true indicating support.", @@ -2674,12 +2935,14 @@ "items": { "type": "string" }, - "x-go-name": "SubjectTypes" + "x-go-name": "SubjectTypes", + "example": "public, pairwise" }, "token_endpoint": { "description": "URL of the OP's OAuth 2.0 Token Endpoint", "type": "string", - "x-go-name": "TokenURL" + "x-go-name": "TokenURL", + "example": "https://playground.ory.sh/ory-hydra/public/oauth2/token" }, "token_endpoint_auth_methods_supported": { "description": "JSON array containing a list of Client Authentication methods supported by this Token Endpoint. The options are\nclient_secret_post, client_secret_basic, client_secret_jwt, and private_key_jwt, as described in Section 9 of OpenID Connect Core 1.0", @@ -2709,33 +2972,7 @@ }, "responses": { "emptyResponse": { - "description": "An empty response" - }, - "genericError": { - "description": "The standard error format", - "schema": { - "type": "object", - "properties": { - "error": { - "type": "string", - "x-go-name": "Name" - }, - "error_code": { - "type": "integer", - "format": "int64", - "x-go-name": "Code" - }, - "error_debug": { - "type": "integer", - "format": "int64", - "x-go-name": "Debug" - }, - "error_hint": { - "type": "string", - "x-go-name": "Hint" - } - } - } + "description": "Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is\ntypically 201." }, "handledConsentRequestList": { "description": "A list of handled consent requests.", @@ -2763,8 +3000,8 @@ "oauth2": { "type": "oauth2", "flow": "accessCode", - "authorizationUrl": "https://your-hydra-instance.com/oauth2/auth", - "tokenUrl": "https://your-hydra-instance.com/oauth2/token", + "authorizationUrl": "/oauth2/auth", + "tokenUrl": "/oauth2/token", "scopes": { "offline": "A scope required when requesting refresh tokens", "openid": "Request an OpenID Connect ID Token" diff --git a/apis/keto-next.json b/apis/keto-next.json new file mode 100644 index 000000000..5721529f3 --- /dev/null +++ b/apis/keto-next.json @@ -0,0 +1,1577 @@ +{ + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "swagger": "2.0", + "info": { + "description": "Package main ORY Keto", + "contact": { + "name": "ORY", + "url": "https://www.ory.sh", + "email": "hi@ory.am" + }, + "license": { + "name": "Apache 2.0", + "url": "https://github.com/ory/keto/blob/master/LICENSE" + }, + "version": "Latest" + }, + "basePath": "/", + "paths": { + "/health/alive": { + "get": { + "description": "This endpoint returns a 200 status code when the HTTP server is up running.\nThis status does currently not include checks whether the database connection is working.\nThis endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set.\n\nBe aware that if you are running multiple nodes of ORY Keto, the health status will never refer to the cluster state, only to a single instance.", + "tags": [ + "health" + ], + "summary": "Check the Alive Status", + "operationId": "isInstanceAlive", + "responses": { + "200": { + "description": "healthStatus", + "schema": { + "$ref": "#/definitions/healthStatus" + } + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/health/ready": { + "get": { + "description": "This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g.\nthe database) are responsive as well.\n\nThis status does currently not include checks whether the database connection is working.\nThis endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set.\n\nBe aware that if you are running multiple nodes of ORY Keto, the health status will never refer to the cluster state, only to a single instance.", + "tags": [ + "health" + ], + "summary": "Check the Readiness Status", + "operationId": "isInstanceReady", + "responses": { + "200": { + "description": "healthStatus", + "schema": { + "$ref": "#/definitions/healthStatus" + } + }, + "503": { + "description": "healthNotReadyStatus", + "schema": { + "$ref": "#/definitions/healthNotReadyStatus" + } + } + } + } + }, + "/policies": { + "get": { + "description": "List Access Control Policies", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "policy" + ], + "operationId": "listPolicies", + "parameters": [ + { + "type": "integer", + "format": "int64", + "x-go-name": "Offset", + "description": "The offset from where to start looking.", + "name": "offset", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "x-go-name": "Limit", + "description": "The maximum amount of policies returned.", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/policyList" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "post": { + "description": "Create an Access Control Policy", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "policy" + ], + "operationId": "createPolicy", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/policy" + } + } + ], + "responses": { + "201": { + "description": "policy", + "schema": { + "$ref": "#/definitions/policy" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/policies/{id}": { + "get": { + "description": "Get an Access Control Policy", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "policy" + ], + "operationId": "getPolicy", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the policy.", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "policy", + "schema": { + "$ref": "#/definitions/policy" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "put": { + "description": "Update an Access Control Policy", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "policy" + ], + "operationId": "updatePolicy", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the policy.", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/policy" + } + } + ], + "responses": { + "200": { + "description": "policy", + "schema": { + "$ref": "#/definitions/policy" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "Delete an Access Control Policy", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "policy" + ], + "operationId": "deletePolicy", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the policy.", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/roles": { + "get": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to retrieve all roles that are stored in the system.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "List all roles", + "operationId": "listRoles", + "parameters": [ + { + "type": "string", + "x-go-name": "Member", + "description": "The id of the member to look up.", + "name": "member", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "x-go-name": "Limit", + "description": "The maximum amount of policies returned.", + "name": "limit", + "in": "query" + }, + { + "type": "integer", + "format": "int64", + "x-go-name": "Offset", + "description": "The offset from where to start looking.", + "name": "offset", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/listRolesResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "post": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to create a new role. You may define members as well but you don't have to.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "Create a role", + "operationId": "createRole", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/role" + } + } + ], + "responses": { + "201": { + "description": "role", + "schema": { + "$ref": "#/definitions/role" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/roles/{id}": { + "get": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to retrieve an existing role. You have to know the role's ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "Get a role by its ID", + "operationId": "getRole", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the role to look up.", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "201": { + "description": "role", + "schema": { + "$ref": "#/definitions/role" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "put": { + "description": "This endpoint allows you to overwrite a role. You have to know the role's ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.", + "operationId": "setRole", + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to delete an existing role. You have to know the role's ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "Get a role by its ID", + "operationId": "deleteRole", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the role to look up.", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/roles/{id}/members": { + "post": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to add members (users, applications, ...) to a specific role. You have to know the role's ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "Add members to a role", + "operationId": "addMembersToRole", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the role to modify.", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/roleMembers" + } + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + }, + "delete": { + "description": "A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.\n\nThis endpoint allows you to remove members (users, applications, ...) from a specific role. You have to know the role's ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "role" + ], + "summary": "Remove members from a role", + "operationId": "removeMembersFromRole", + "parameters": [ + { + "type": "string", + "x-go-name": "ID", + "description": "The id of the role to modify.", + "name": "id", + "in": "path", + "required": true + }, + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/roleMembers" + } + } + ], + "responses": { + "204": { + "$ref": "#/responses/emptyResponse" + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/version": { + "get": { + "description": "This endpoint returns the version as `{ \"version\": \"VERSION\" }`. The version is only correct with the prebuilt binary and not custom builds.", + "tags": [ + "version" + ], + "summary": "Get the version of Keto", + "operationId": "getVersion", + "responses": { + "200": { + "description": "version", + "schema": { + "$ref": "#/definitions/version" + } + } + } + } + }, + "/warden/oauth2/access-tokens/authorize": { + "post": { + "description": "Checks if a token is valid and if the token subject is allowed to perform an action on a resource.\nThis endpoint requires a token, a scope, a resource name, an action name and a context.\n\n\nIf a token is expired/invalid, has not been granted the requested scope or the subject is not allowed to\nperform the action on the resource, this endpoint returns a 200 response with `{ \"allowed\": false }`.\n\n\nThis endpoint passes all data from the upstream OAuth 2.0 token introspection endpoint. If you use ORY Hydra as an\nupstream OAuth 2.0 provider, data set through the `accessTokenExtra` field in the consent flow will be included in this\nresponse as well.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "warden" + ], + "summary": "Check if an OAuth 2.0 access token is authorized to access a resource", + "operationId": "isOAuth2AccessTokenAuthorized", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/wardenOAuth2AccessTokenAuthorizationRequest" + } + } + ], + "responses": { + "200": { + "description": "wardenOAuth2AccessTokenAuthorizationResponse", + "schema": { + "$ref": "#/definitions/wardenOAuth2AccessTokenAuthorizationResponse" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/warden/oauth2/clients/authorize": { + "post": { + "description": "Checks if an OAuth 2.0 Client provided the correct access credentials and and if the client is allowed to perform\nan action on a resource. This endpoint requires a client id, a client secret, a scope, a resource name, an action name and a context.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "warden" + ], + "summary": "Check if an OAuth 2.0 Client is authorized to access a resource", + "operationId": "isOAuth2ClientAuthorized", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/wardenOAuth2ClientAuthorizationRequest" + } + } + ], + "responses": { + "200": { + "description": "wardenOAuth2ClientAuthorizationResponse", + "schema": { + "$ref": "#/definitions/wardenOAuth2ClientAuthorizationResponse" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + }, + "/warden/subjects/authorize": { + "post": { + "description": "Checks if a subject (e.g. user ID, API key, ...) is allowed to perform a certain action on a resource.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "schemes": [ + "http", + "https" + ], + "tags": [ + "warden" + ], + "summary": "Check if a subject is authorized to access a resource", + "operationId": "isSubjectAuthorized", + "parameters": [ + { + "name": "Body", + "in": "body", + "schema": { + "$ref": "#/definitions/WardenSubjectAuthorizationRequest" + } + } + ], + "responses": { + "200": { + "description": "wardenSubjectAuthorizationResponse", + "schema": { + "$ref": "#/definitions/wardenSubjectAuthorizationResponse" + } + }, + "401": { + "$ref": "#/responses/genericError" + }, + "403": { + "$ref": "#/responses/genericError" + }, + "500": { + "$ref": "#/responses/genericError" + } + } + } + } + }, + "definitions": { + "AuthenticationOAuth2ClientCredentialsRequest": { + "type": "object", + "properties": { + "client_id": { + "description": "Token is the token to introspect.", + "type": "string", + "x-go-name": "ClientID" + }, + "client_secret": { + "type": "string", + "x-go-name": "ClientSecret" + }, + "scope": { + "description": "Scope is an array of scopes that are required.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Scopes" + } + }, + "x-go-package": "github.com/ory/keto/authentication" + }, + "AuthenticationOAuth2IntrospectionRequest": { + "type": "object", + "properties": { + "scope": { + "description": "Scope is an array of scopes that are required.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Scope" + }, + "token": { + "description": "Token is the token to introspect.", + "type": "string", + "x-go-name": "Token" + } + }, + "x-go-package": "github.com/ory/keto/authentication" + }, + "Authenticator": { + "type": "object", + "x-go-package": "github.com/ory/keto/authentication" + }, + "Firewall": { + "type": "object", + "title": "Firewall offers various validation strategies for access tokens.", + "x-go-package": "github.com/ory/keto/warden" + }, + "Handler": { + "type": "object", + "properties": { + "H": { + "$ref": "#/definitions/Writer" + }, + "Manager": { + "$ref": "#/definitions/Manager" + } + }, + "x-go-package": "github.com/ory/keto/role" + }, + "IntrospectionResponse": { + "type": "object", + "properties": { + "active": { + "type": "boolean", + "x-go-name": "Active" + }, + "aud": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Audience" + }, + "client_id": { + "type": "string", + "x-go-name": "ClientID" + }, + "exp": { + "type": "integer", + "format": "int64", + "x-go-name": "ExpiresAt" + }, + "ext": { + "description": "Session represents arbitrary session data.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Extra" + }, + "iat": { + "type": "integer", + "format": "int64", + "x-go-name": "IssuedAt" + }, + "iss": { + "type": "string", + "x-go-name": "Issuer" + }, + "nbf": { + "type": "integer", + "format": "int64", + "x-go-name": "NotBefore" + }, + "scope": { + "type": "string", + "x-go-name": "Scope" + }, + "sub": { + "description": "Here, it's sub", + "type": "string", + "x-go-name": "Subject" + }, + "token_type": { + "type": "string", + "x-go-name": "TokenType" + }, + "username": { + "type": "string", + "x-go-name": "Username" + } + }, + "x-go-package": "github.com/ory/keto/authentication" + }, + "Manager": { + "type": "object", + "x-go-package": "github.com/ory/keto/role" + }, + "OAuth2ClientCredentialsAuthentication": { + "type": "object", + "x-go-package": "github.com/ory/keto/authentication" + }, + "OAuth2IntrospectionAuthentication": { + "type": "object", + "x-go-package": "github.com/ory/keto/authentication" + }, + "Session": { + "type": "object", + "properties": { + "GetSubject": { + "type": "string" + } + }, + "x-go-package": "github.com/ory/keto/authentication" + }, + "WardenSubjectAuthorizationRequest": { + "type": "object", + "title": "AccessRequest is the warden's request object.", + "properties": { + "action": { + "description": "Action is the action that is requested on the resource.", + "type": "string", + "x-go-name": "Action" + }, + "context": { + "description": "Context is the request's environmental context.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Context" + }, + "resource": { + "description": "Resource is the resource that access is requested to.", + "type": "string", + "x-go-name": "Resource" + }, + "subject": { + "description": "Subejct is the subject that is requesting access.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "AccessRequest", + "x-go-package": "github.com/ory/keto/warden" + }, + "Writer": { + "description": "Writer is a helper to write arbitrary data to a ResponseWriter", + "type": "object", + "x-go-package": "github.com/ory/keto/vendor/github.com/ory/herodot" + }, + "authenticationDefaultSession": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "DefaultSession", + "x-go-package": "github.com/ory/keto/authentication" + }, + "authenticationOAuth2ClientCredentialsSession": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "OAuth2ClientCredentialsSession", + "x-go-package": "github.com/ory/keto/authentication" + }, + "authenticationOAuth2Session": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "aud": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Audience" + }, + "client_id": { + "description": "ClientID is the id of the OAuth2 client that requested the token.", + "type": "string", + "x-go-name": "ClientID" + }, + "exp": { + "description": "ExpiresAt is the expiry timestamp.", + "type": "string", + "format": "date-time", + "x-go-name": "ExpiresAt" + }, + "iat": { + "description": "IssuedAt is the token creation time stamp.", + "type": "string", + "format": "date-time", + "x-go-name": "IssuedAt" + }, + "iss": { + "description": "Issuer is the id of the issuer, typically an hydra instance.", + "type": "string", + "x-go-name": "Issuer" + }, + "nbf": { + "type": "string", + "format": "date-time", + "x-go-name": "NotBefore" + }, + "scope": { + "description": "GrantedScopes is a list of scopes that the subject authorized when asked for consent.", + "type": "string", + "x-go-name": "GrantedScopes" + }, + "session": { + "description": "Session represents arbitrary session data.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Extra" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + }, + "username": { + "type": "string", + "x-go-name": "Username" + } + }, + "x-go-name": "OAuth2Session", + "x-go-package": "github.com/ory/keto/authentication" + }, + "healthNotReadyStatus": { + "type": "object", + "properties": { + "errors": { + "description": "Errors contains a list of errors that caused the not ready status.", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "x-go-name": "Errors" + } + }, + "x-go-name": "swaggerNotReadyStatus", + "x-go-package": "github.com/ory/keto/health" + }, + "healthStatus": { + "type": "object", + "properties": { + "status": { + "description": "Status always contains \"ok\".", + "type": "string", + "x-go-name": "Status" + } + }, + "x-go-name": "swaggerHealthStatus", + "x-go-package": "github.com/ory/keto/health" + }, + "policy": { + "type": "object", + "properties": { + "actions": { + "description": "Actions impacted by the policy.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Actions" + }, + "conditions": { + "description": "Conditions under which the policy is active.", + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "options": { + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Options" + }, + "type": { + "type": "string", + "x-go-name": "Type" + } + } + }, + "x-go-name": "Conditions" + }, + "description": { + "description": "Description of the policy.", + "type": "string", + "x-go-name": "Description" + }, + "effect": { + "description": "Effect of the policy", + "type": "string", + "x-go-name": "Effect" + }, + "id": { + "description": "ID of the policy.", + "type": "string", + "x-go-name": "ID" + }, + "resources": { + "description": "Resources impacted by the policy.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Resources" + }, + "subjects": { + "description": "Subjects impacted by the policy.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Subjects" + } + }, + "x-go-name": "swaggerPolicy", + "x-go-package": "github.com/ory/keto/policy" + }, + "role": { + "description": "Role represents a group of users that share the same role. A role could be an administrator, a moderator, a regular\nuser or some other sort of role.", + "type": "object", + "properties": { + "id": { + "description": "ID is the role's unique id.", + "type": "string", + "x-go-name": "ID" + }, + "members": { + "description": "Members is who belongs to the role.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Members" + } + }, + "x-go-name": "Role", + "x-go-package": "github.com/ory/keto/role" + }, + "roleMembers": { + "type": "object", + "properties": { + "members": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Members" + } + }, + "x-go-name": "membersRequest", + "x-go-package": "github.com/ory/keto/role" + }, + "swaggerCreatePolicyParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/policy" + } + }, + "x-go-package": "github.com/ory/keto/policy" + }, + "swaggerDoesWardenAllowAccessRequestParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/WardenSubjectAuthorizationRequest" + } + }, + "x-go-package": "github.com/ory/keto/warden" + }, + "swaggerDoesWardenAllowClientRequestParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/wardenOAuth2ClientAuthorizationRequest" + } + }, + "x-go-package": "github.com/ory/keto/warden" + }, + "swaggerDoesWardenAllowTokenAccessRequestParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/wardenOAuth2AccessTokenAuthorizationRequest" + } + }, + "x-go-package": "github.com/ory/keto/warden" + }, + "swaggerGetPolicyParameters": { + "type": "object", + "properties": { + "id": { + "description": "The id of the policy.\nin: path", + "type": "string", + "x-go-name": "ID" + } + }, + "x-go-package": "github.com/ory/keto/policy" + }, + "swaggerListPolicyParameters": { + "type": "object", + "properties": { + "limit": { + "description": "The maximum amount of policies returned.\nin: query", + "type": "integer", + "format": "int64", + "x-go-name": "Limit" + }, + "offset": { + "description": "The offset from where to start looking.\nin: query", + "type": "integer", + "format": "int64", + "x-go-name": "Offset" + } + }, + "x-go-package": "github.com/ory/keto/policy" + }, + "swaggerListPolicyResponse": { + "description": "A policy", + "type": "object", + "properties": { + "Body": { + "description": "in: body\ntype: array", + "type": "array", + "items": { + "$ref": "#/definitions/policy" + } + } + }, + "x-go-package": "github.com/ory/keto/policy" + }, + "swaggerUpdatePolicyParameters": { + "type": "object", + "properties": { + "Body": { + "$ref": "#/definitions/policy" + }, + "id": { + "description": "The id of the policy.\nin: path", + "type": "string", + "x-go-name": "ID" + } + }, + "x-go-package": "github.com/ory/keto/policy" + }, + "swaggerWardenBaseRequest": { + "description": "swager:model authorizedBaseRequest", + "type": "object", + "properties": { + "action": { + "description": "Action is the action that is requested on the resource.", + "type": "string", + "x-go-name": "Action" + }, + "context": { + "description": "Context is the request's environmental context.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Context" + }, + "resource": { + "description": "Resource is the resource that access is requested to.", + "type": "string", + "x-go-name": "Resource" + } + }, + "x-go-package": "github.com/ory/keto/warden" + }, + "version": { + "type": "object", + "properties": { + "version": { + "type": "string", + "x-go-name": "Version" + } + }, + "x-go-name": "swaggerVersion", + "x-go-package": "github.com/ory/keto/health" + }, + "wardenOAuth2AccessTokenAuthorizationRequest": { + "type": "object", + "properties": { + "action": { + "description": "Action is the action that is requested on the resource.", + "type": "string", + "x-go-name": "Action" + }, + "context": { + "description": "Context is the request's environmental context.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Context" + }, + "resource": { + "description": "Resource is the resource that access is requested to.", + "type": "string", + "x-go-name": "Resource" + }, + "scope": { + "description": "Scope is an array of scopes that are required.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Scope" + }, + "token": { + "description": "Token is the token to introspect.", + "type": "string", + "x-go-name": "Token" + } + }, + "x-go-name": "swaggerWardenTokenAccessRequest", + "x-go-package": "github.com/ory/keto/warden" + }, + "wardenOAuth2AccessTokenAuthorizationResponse": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "aud": { + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Audience" + }, + "client_id": { + "description": "ClientID is the id of the OAuth2 client that requested the token.", + "type": "string", + "x-go-name": "ClientID" + }, + "exp": { + "description": "ExpiresAt is the expiry timestamp.", + "type": "string", + "format": "date-time", + "x-go-name": "ExpiresAt" + }, + "iat": { + "description": "IssuedAt is the token creation time stamp.", + "type": "string", + "format": "date-time", + "x-go-name": "IssuedAt" + }, + "iss": { + "description": "Issuer is the id of the issuer, typically an hydra instance.", + "type": "string", + "x-go-name": "Issuer" + }, + "nbf": { + "type": "string", + "format": "date-time", + "x-go-name": "NotBefore" + }, + "scope": { + "description": "GrantedScopes is a list of scopes that the subject authorized when asked for consent.", + "type": "string", + "x-go-name": "GrantedScopes" + }, + "session": { + "description": "Session represents arbitrary session data.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Extra" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + }, + "username": { + "type": "string", + "x-go-name": "Username" + } + }, + "x-go-name": "oauth2Authorization", + "x-go-package": "github.com/ory/keto/warden" + }, + "wardenOAuth2ClientAuthorizationRequest": { + "type": "object", + "properties": { + "action": { + "description": "Action is the action that is requested on the resource.", + "type": "string", + "x-go-name": "Action" + }, + "client_id": { + "description": "Token is the token to introspect.", + "type": "string", + "x-go-name": "ClientID" + }, + "client_secret": { + "type": "string", + "x-go-name": "ClientSecret" + }, + "context": { + "description": "Context is the request's environmental context.", + "type": "object", + "additionalProperties": { + "type": "object" + }, + "x-go-name": "Context" + }, + "resource": { + "description": "Resource is the resource that access is requested to.", + "type": "string", + "x-go-name": "Resource" + }, + "scope": { + "description": "Scope is an array of scopes that are required.", + "type": "array", + "items": { + "type": "string" + }, + "x-go-name": "Scopes" + } + }, + "x-go-name": "swaggerWardenClientAccessRequest", + "x-go-package": "github.com/ory/keto/warden" + }, + "wardenOAuth2ClientAuthorizationResponse": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "oauth2ClientAuthorization", + "x-go-package": "github.com/ory/keto/warden" + }, + "wardenSubjectAuthorizationResponse": { + "type": "object", + "properties": { + "allowed": { + "description": "Allowed is true if the request is allowed and false otherwise.", + "type": "boolean", + "x-go-name": "Allowed" + }, + "sub": { + "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", + "type": "string", + "x-go-name": "Subject" + } + }, + "x-go-name": "subjectAuthorization", + "x-go-package": "github.com/ory/keto/warden" + } + }, + "responses": { + "emptyResponse": { + "description": "An empty response" + }, + "genericError": { + "description": "The standard error format", + "schema": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int64", + "x-go-name": "Code" + }, + "details": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": { + "type": "object" + } + }, + "x-go-name": "Details" + }, + "message": { + "type": "string", + "x-go-name": "Message" + }, + "reason": { + "type": "string", + "x-go-name": "Reason" + }, + "request": { + "type": "string", + "x-go-name": "Request" + }, + "status": { + "type": "string", + "x-go-name": "Status" + } + } + } + }, + "listRolesResponse": { + "description": "A list of roles the member is belonging to", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/role" + } + } + }, + "policyList": { + "description": "A policy", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/policy" + } + } + } + } +} \ No newline at end of file diff --git a/apis/oathkeeper.json b/apis/oathkeeper.json index 12a00b2f3..dc2486c83 100644 --- a/apis/oathkeeper.json +++ b/apis/oathkeeper.json @@ -380,134 +380,6 @@ } }, "definitions": { - "AuthenticationOAuth2ClientCredentialsRequest": { - "type": "object", - "properties": { - "id": { - "description": "Token is the token to introspect.", - "type": "string", - "x-go-name": "ClientID" - }, - "scope": { - "description": "Scope is an array of scopes that are required.", - "type": "array", - "items": { - "type": "string" - }, - "x-go-name": "Scopes" - }, - "secret": { - "type": "string", - "x-go-name": "ClientSecret" - } - }, - "x-go-package": "github.com/ory/oathkeeper/vendor/github.com/ory/keto/authentication" - }, - "AuthenticationOAuth2IntrospectionRequest": { - "type": "object", - "properties": { - "scope": { - "description": "Scope is an array of scopes that are required.", - "type": "array", - "items": { - "type": "string" - }, - "x-go-name": "Scope" - }, - "token": { - "description": "Token is the token to introspect.", - "type": "string", - "x-go-name": "Token" - } - }, - "x-go-package": "github.com/ory/oathkeeper/vendor/github.com/ory/keto/authentication" - }, - "Authenticator": { - "type": "object", - "x-go-package": "github.com/ory/oathkeeper/vendor/github.com/ory/keto/authentication" - }, - "IntrospectionResponse": { - "type": "object", - "properties": { - "active": { - "type": "boolean", - "x-go-name": "Active" - }, - "aud": { - "type": "array", - "items": { - "type": "string" - }, - "x-go-name": "Audience" - }, - "client_id": { - "type": "string", - "x-go-name": "ClientID" - }, - "exp": { - "type": "integer", - "format": "int64", - "x-go-name": "ExpiresAt" - }, - "ext": { - "description": "Session represents arbitrary session data.", - "type": "object", - "additionalProperties": { - "type": "object" - }, - "x-go-name": "Extra" - }, - "iat": { - "type": "integer", - "format": "int64", - "x-go-name": "IssuedAt" - }, - "iss": { - "type": "string", - "x-go-name": "Issuer" - }, - "nbf": { - "type": "integer", - "format": "int64", - "x-go-name": "NotBefore" - }, - "scope": { - "type": "string", - "x-go-name": "Scope" - }, - "sub": { - "description": "Here, it's sub", - "type": "string", - "x-go-name": "Subject" - }, - "token_type": { - "type": "string", - "x-go-name": "TokenType" - }, - "username": { - "type": "string", - "x-go-name": "Username" - } - }, - "x-go-package": "github.com/ory/oathkeeper/vendor/github.com/ory/keto/authentication" - }, - "OAuth2ClientCredentialsAuthentication": { - "type": "object", - "x-go-package": "github.com/ory/oathkeeper/vendor/github.com/ory/keto/authentication" - }, - "OAuth2IntrospectionAuthentication": { - "type": "object", - "x-go-package": "github.com/ory/oathkeeper/vendor/github.com/ory/keto/authentication" - }, - "Session": { - "type": "object", - "properties": { - "GetSubject": { - "type": "string" - } - }, - "x-go-package": "github.com/ory/oathkeeper/vendor/github.com/ory/keto/authentication" - }, "Upstream": { "type": "object", "properties": { @@ -529,108 +401,6 @@ }, "x-go-package": "github.com/ory/oathkeeper/rule" }, - "authenticationDefaultSession": { - "type": "object", - "properties": { - "allowed": { - "description": "Allowed is true if the request is allowed and false otherwise.", - "type": "boolean", - "x-go-name": "Allowed" - }, - "sub": { - "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", - "type": "string", - "x-go-name": "Subject" - } - }, - "x-go-name": "DefaultSession", - "x-go-package": "github.com/ory/oathkeeper/vendor/github.com/ory/keto/authentication" - }, - "authenticationOAuth2ClientCredentialsSession": { - "type": "object", - "properties": { - "allowed": { - "description": "Allowed is true if the request is allowed and false otherwise.", - "type": "boolean", - "x-go-name": "Allowed" - }, - "sub": { - "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", - "type": "string", - "x-go-name": "Subject" - } - }, - "x-go-name": "OAuth2ClientCredentialsSession", - "x-go-package": "github.com/ory/oathkeeper/vendor/github.com/ory/keto/authentication" - }, - "authenticationOAuth2Session": { - "type": "object", - "properties": { - "allowed": { - "description": "Allowed is true if the request is allowed and false otherwise.", - "type": "boolean", - "x-go-name": "Allowed" - }, - "aud": { - "type": "array", - "items": { - "type": "string" - }, - "x-go-name": "Audience" - }, - "client_id": { - "description": "ClientID is the id of the OAuth2 client that requested the token.", - "type": "string", - "x-go-name": "ClientID" - }, - "exp": { - "description": "ExpiresAt is the expiry timestamp.", - "type": "string", - "format": "date-time", - "x-go-name": "ExpiresAt" - }, - "iat": { - "description": "IssuedAt is the token creation time stamp.", - "type": "string", - "format": "date-time", - "x-go-name": "IssuedAt" - }, - "iss": { - "description": "Issuer is the id of the issuer, typically an hydra instance.", - "type": "string", - "x-go-name": "Issuer" - }, - "nbf": { - "type": "string", - "format": "date-time", - "x-go-name": "NotBefore" - }, - "scope": { - "description": "GrantedScopes is a list of scopes that the subject authorized when asked for consent.", - "type": "string", - "x-go-name": "GrantedScopes" - }, - "session": { - "description": "Session represents arbitrary session data.", - "type": "object", - "additionalProperties": { - "type": "object" - }, - "x-go-name": "Extra" - }, - "sub": { - "description": "Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app.\nThis is usually a uuid but you can choose a urn or some other id too.", - "type": "string", - "x-go-name": "Subject" - }, - "username": { - "type": "string", - "x-go-name": "Username" - } - }, - "x-go-name": "OAuth2Session", - "x-go-package": "github.com/ory/oathkeeper/vendor/github.com/ory/keto/authentication" - }, "healthNotReadyStatus": { "type": "object", "properties": { diff --git a/docs/ecosystem/cloud-native.md b/docs/ecosystem/cloud-native.md new file mode 100644 index 000000000..978ae2efe --- /dev/null +++ b/docs/ecosystem/cloud-native.md @@ -0,0 +1,101 @@ +--- +id: ecosystem-cloud-native +title: Cloud Native Architecture +--- + +All ORY technology is built cloud native and follows [12factor.net](https://www.12factor.net) principles. + +## 12 Factor Concepts + +The following is a collection of core concepts to ORY's technological ideology. They are summaries of the +[12factor principles](https://www.12factor.net) written by Adam Wiggins. + +### Codebase: One codebase tracked in revision control, many deploys + +A twelve-factor app is always tracked in a version control system, such as Git, Mercurial, or Subversion. A copy of the +revision tracking database is known as a code repository, often shortened to code repo or just repo. + +A codebase is any single repo (in a centralized revision control system like Subversion), or any set of repos who share +a root commit (in a decentralized revision control system like Git). + +### Dependencies: Explicitly declare and isolate dependencies + +A twelve-factor app never relies on implicit existence of system-wide packages. It declares all dependencies, +completely and exactly, via a dependency declaration manifest. Furthermore, it uses a dependency isolation tool +during execution to ensure that no implicit dependencies “leak in” from the surrounding system. The full and explicit +dependency specification is applied uniformly to both production and development. + +### Config: Store config in the environment + +The twelve-factor app stores config in environment variables (often shortened to env vars or env). Env vars are easy +to change between deploys without changing any code; unlike config files, there is little chance of them being checked +into the code repo accidentally; and unlike custom config files, or other config mechanisms such as Java System +Properties, they are a language- and OS-agnostic standard. + +### Backing services: Treat backing services as attached resources + +A backing service is any service the app consumes over the network as part of its normal operation. Examples +include datastores (such as MySQL or CouchDB), messaging/queueing systems (such as RabbitMQ or Beanstalkd), SMTP +services for outbound email (such as Postfix), and caching systems (such as Memcached). + +The code for a twelve-factor app makes no distinction between local and third party services. To the app, both are +attached resources, accessed via a URL or other locator/credentials stored in the config. A deploy of the twelve-factor +app should be able to swap out a local MySQL database with one managed by a third party (such as Amazon RDS) without +any changes to the app’s code. Likewise, a local SMTP server could be swapped with a third-party SMTP service +(such as Postmark) without code changes. In both cases, only the resource handle in the config needs to change. + +### Build, release, run: Strictly separate build and run stages + +The twelve-factor app uses strict separation between the build, release, and run stages. For example, it is +impossible to make changes to the code at runtime, since there is no way to propagate those changes back +to the build stage. + +### Processes: Execute the app as one or more stateless processes + +Twelve-factor processes are stateless and share-nothing. Any data that needs to persist must be stored +in a stateful backing service, typically a database. + +### Port binding: Export services via port binding + +The twelve-factor app is completely self-contained and does not rely on runtime injection of a webserver into +the execution environment to create a web-facing service. The web app exports HTTP as a service by binding to a +port, and listening to requests coming in on that port. + +### Concurrency: Scale out via the process model + +In the twelve-factor app, processes are a first class citizen. Processes in the twelve-factor app take strong +cues from the unix process model for running service daemons. Using this model, the developer can architect +their app to handle diverse workloads by assigning each type of work to a process type. For example, HTTP requests may +be handled by a web process, and long-running background tasks handled by a worker process. + +### Disposability: Maximize robustness with fast startup and graceful shutdown + +The twelve-factor app’s processes are disposable, meaning they can be started or stopped at a moment’s notice. +This facilitates fast elastic scaling, rapid deployment of code or config changes, and robustness of production deploys. + +Processes should strive to minimize startup time. Ideally, a process takes a few seconds from the time the launch +command is executed until the process is up and ready to receive requests or jobs. Short startup time provides more +agility for the release process and scaling up; and it aids robustness, because the process manager can more easily +move processes to new physical machines when warranted. + +Processes shut down gracefully when they receive a SIGTERM signal from the process manager. For a web process, graceful +shutdown is achieved by ceasing to listen on the service port (thereby refusing any new requests), allowing any current +requests to finish, and then exiting. Implicit in this model is that HTTP requests are short (no more than a few seconds), +or in the case of long polling, the client should seamlessly attempt to reconnect when the connection is lost. + +### Admin processes: Run admin/management tasks as one-off processes + +The process formation is the array of processes that are used to do the app’s regular business (such as handling web +requests) as it runs. Separately, developers will often wish to do one-off administrative or maintenance tasks for +the app, such as running database migrations (e.g. `hydra migrate sql`). + +One-off admin processes should be run in an identical environment as the regular long-running processes of the app. +They run against a release, using the same codebase and config as any process run against that release. Admin code +must ship with application code to avoid synchronization issues. + +### Logs: Treat logs as event streams + +A twelve-factor app never concerns itself with routing or storage of its output stream. It should not attempt +to write to or manage logfiles. Instead, each running process writes its event stream, unbuffered, to stdout. +During local development, the developer will view this stream in the foreground of their terminal to observe +the app’s behavior. \ No newline at end of file diff --git a/docs/ecosystem/examples.md b/docs/ecosystem/examples.md new file mode 100644 index 000000000..4e6cca992 --- /dev/null +++ b/docs/ecosystem/examples.md @@ -0,0 +1,15 @@ +--- +id: ecosystem-examples +title: Learn By Example +--- + +Learning by example is a good way to get started, but **it does not replace** studying the docs. + +## Example Environments + +The [ory/examples](https://github.com/ory/examples) repository contains numerous examples of setting up these services +individually, and together. Check them out to get a real-world view of this ecosystem! + +## Kubernetes + +You can find all kubernetes-related examples and definitions at [github.com/ory/k8s](https://github.com/ory/k8s) diff --git a/docs/ecosystem/overview.md b/docs/ecosystem/overview.md new file mode 100644 index 000000000..ae66314e1 --- /dev/null +++ b/docs/ecosystem/overview.md @@ -0,0 +1,61 @@ +--- +id: ecosystem-overview +title: Overview +--- + +We provide an open source ecosystem of services with clear boundaries that solve authentication and authorization: + +- ORY Hydra is an OAuth 2.0 and OpenID Connect provider. +- ORY Oathkeeper is an Identity and Access Proxy. +- ORY Keto is an access control server. +- *TBA is an identity management server.* + +Each service works standalone but you can also combine them to get the full feature set. If you've never heard of an Identity & Access Proxy +before, or you want to learn more about the individual services and how they play together, stick with us through the +next paragraphs. + +Almost every application has the concept of users and permissions. An anonymous user, for example, is allowed to +read blog posts while certain authenticated users are allowed to write blog posts. While this is the basis for most +applications out there, access control becomes increasingly complex as an application grows. What started out with +a user's username and password now shifted to machine-2-machine interaction, third party developers accessing your +user's data, and maybe even a micro service system architecture. + +Our products solve the simplest use case and give you the ability to instantly ready the system for more complex scenarios without +painful and slow upgrade processes. + +The first service, which is to be announced soon, focuses on identity management. Instead of re-writing login, logout, +activation emails, 2fa, and worring about GDPR, you spin up a docker image and write a simple UI for it in the language +or framework of your choice. + +**ORY Hydra** enables you to become an OAuth 2.0 and OpenID Connect provider. If you're not writing a basic web app but something +that has to work on different devices, that has machine-2-machine interaction, or enables third-party developers to use +your API (and pay for it), then this is what you're looking for. ORY Hydra is not identity management, though. Instead, +it connects to your existing identity management (e.g. the one from the paragraph above, or your MySQL+PHP login service, +or your Federated SAML SSO) and is capable of issuing, in a secure and OpenID Certified manner, access, refresh, and ID tokens. +Of course, it's shipped as a 5MB Docker Image with almost no configuration required. + +Now that your users access your application through, for example, a React/Angular app and a REST api, you need a way +to authenticate the user and to check if he/she has the necessary permissions (we call this "access control" from now on). +One way would be, of course, to add these checks in your code. Another is to deploy the 5MB **ORY Oathkeeper** Docker Image, +define access rules for your API endpoints (e.g. OAuth 2.0 Access Token + certain set of permissions, a valid JSON Web Token, +a valid SAML assertion, ...) and put it - like a firewall - in front of your services. + +You might start out with a simple permission system. You've got different roles: anonymous users (not logged in), +authenticated users (logged in), and administrators. At some point however, the system gets more complex. You want +to distinct permissions based on the user's organization, the access time (think time lock in banking), or the billing +plan he/she's on. Big cloud providers such as Amazon Web Services or Google solve this using "Access Control Policies". +These policies represent flexible rules and allow you to express complex access control scenarios. You could, of course, +write your own system or spend a bit of time educating yourself about RBAC, ACL, ABAC, ACP - or (you probably already guessed it) - +boot up the 5MB **ORY Keto** Docker Image. ORY Keto is able to authenticate different types of credentials (e.g. OAuth 2.0 Access +Tokens, SAML Assertions, JSON Web Tokens, ...) and allows you to define advanced permission rules ("Access Control Policies"). +And there's of course an endpoint that tells you if a certain set of credentials (e.g. an OAuth 2.0 Access Token) is allowed +to modify that blog post. + +![The full ORY Ecosystem](../../images/docs/ecosystem/ory-ecosystem.png) +If you were to use the full ORY Ecosystem, it would probably look something like this. Keep in mind that any component +shown here can be replaced or removed, depending on your use case. + +Now you know what this ecosystem has to offer you. To get some more information on the services, read the developer guide by +selecting the software of your choice from the navigation on the left! + +Contact us at [hi@ory.sh](mailto:hi@ory.sh) if you need consulting with your specific project. diff --git a/docs/ecosystem/sqa.md b/docs/ecosystem/sqa.md new file mode 100644 index 000000000..639b04989 --- /dev/null +++ b/docs/ecosystem/sqa.md @@ -0,0 +1,170 @@ +--- +id: ecosystem-sqa +title: Software Quality Assurance +--- + +Our goal is to provide you with the fastest and most reliable open source services. To achieve this goal, we collect +metrics on endpoint performance and send a **fully anonymized** telemetry report ("anonymous usage statistics") to our +servers. This data helps us understand how changes impact performance and stability of our open source service and +identify potential issues. + +We are committed to full transparency on what data we transmit why and how. The source code of the telemetry package +is completely open source and located [here](https://github.com/ory/x/blob/master/metricsx). If you do not wish to help +us improve our products by sharing telemetry data, it is possible to [turn off this feature](#disabling-telemetry). + +## Data Processing + +We want to give you a thorough understanding why we collect this data, how we collect it, and what we do with it, +as well as real-world examples of how this data improved a product. + +The data processing pipeline has the following steps: + +1. Telemetry data is collected at each service. +2. Periodically this data is sent to the [segment.com](https://segment.com/) API. +3. Segment forwards this data to a private AWS S3 Bucket owned by the ORY organization. The data is not shared +with any other party. +4. The AWS S3 Bucket(s) are periodically downloaded onto on of our on-premise servers. +5. The downloaded data is extracted, filtered, processed, and analyzed. The output is a CSV report which we analyze +using Open Office. + +We built this pipeline the data with the following goals: + +1. Be able to say how many production deployments exist. +- Understand which features are used and how. +- Understand how much throughput deployments handle. +- Evaluate how frequently specific features are used. +- Detect issues introduced by new features (e.g. buggy releases). +- Identify problems at scale (e.g. slow endpoints). +- Understand which versions are deployed. + +The following real-world outcomes have been achieved using this data (excerpt): + +1. We were able to identify that ORY Hydra's Warden and Policy API were heavily underused and decided to move these +APIs to a different product (ORY Keto) which has been received very well by the community. +2. A v1.0.0 beta released caused a heavy increase in response times for certain environments at one Consent API endpoint. +We identified that a missing database index caused this issue and resolved it in the next patch. +3. We learned that many developers still run old versions, sometimes with critical security vulnerabilities. To resolve this, +we improved the release process and introduced a release newsletter. Use of vulnerable versions has dropped by 20% since then. +4. A heavy uptake in usage of ORY Keto showed us that we need to provide certain migration tools for an update that +introduces breaking changes. We were under the impression that the service was only used in test environments. + +## Disabling Telemetry + +You can opt out of telemetry reports with the `--disable-telemetry` flag and also by +setting environment variable `DISABLE_TELEMETRY=1`. + +Disabling telemetry does not have any downsides, except for us not being able to improve the product. + +## Data Privacy + +To protect your privacy, we filter out any data that could identify you or your users. We are taking the following +measures to protect your privacy: + +1. We only transmit information on how often endpoints are requested, how fast they respond and what HTTP status code was sent. +2. We filter out any query parameters, headers, response and request bodies and path parameters. A full list of transmitted +URL paths is listed in section [Request telemetry](#request-telemetry). +4. **We are unable to see or store the IP address of your host**, as the +[IP is set to `0.0.0.0`](https://github.com/ory/x/blob/master/metricsx/middleware.go) when transmitting data to our metrics aggregator. +5. We do not transmit any environment information from the host, except: + * Operating system id (windows, linux, osx) + * The target architecture (amd64, darwin, ...) + * Number of CPUs available + * Binary build time, git hash, git tag + * Memory consumption of the process + +The information is stored in an aggregated format without any personally identifiable information. + +### Identification + +To identify an installation and group together clusters, we create a SHA-256 hash of unique information (e.g. host, port) +for identification. Additionally, each running instance is identified using an unique identifier which is set every +time the service starts. The identifier is a Universally Unique Identifier (V4) and is thus a cryptographically safe +random string. Identification is triggered when we are confident that the instance is not a test instance (e.g. one +of the tutorials or a local installation). + +We collect the following system metrics: + +* `goarch`: The target architecture of the service binary. +* `goos`: The target system of the service binary. +* `numCpu`: The number of CPUs available. +* `runtimeVersion`: The go version used to create the binary. +* `version`: The version of this binary. +* `hash`: The git hash of this binary. +* `buildTime`: The build time of this binary. + +### Request telemetry + +The ip addresses of both host and client are anonymized to `0.0.0.0`. Any identifiable information in the URL path and query is hashed with +sha256 using a randomly assigned uuid v4 salt: + +* `/clients/foo` with salt `ABCDEFGH` becomes `/clients/sha256("foo|ABCDEFGH")`: `/clients/0301424a80469ad03a208de925563a97ec6ab2f9dc7a2ad71b2ded85a7f7a7af` +* `/policies?owner=foo` with salt `ABCDEFGH` becomes `/policies?owner=sha256("foo|ABCDEFGH")`: `/policies?owner=0301424a80469ad03a208de925563a97ec6ab2f9dc7a2ad71b2ded85a7f7a7af`). + +### Source Code + +The full code-base is [open source](https://github.com/ory/metrics-middleware). + +### Data Example + +The following code snippet represents two raw event types (`page` and `identify`) collected by a real ORY Hydra instance: + +```json +[ + { + "context": { + "ip": "0.0.0.0", + "library": { + "name": "analytics-go", + "version": "3.0.0" + } + }, + "messageId": "21999137-d1d2-4102-9a94-57beed5e5fca", + "timestamp": "2018-01-18T18:41:37.028Z", + "traits": { + "buildTime": "2018-01-18 18:41:35.6222348 +0000 UTC", + "goarch": "amd64", + "goos": "windows", + "hash": "undefined", + "instanceId": "c2bdd39c-3b0a-4f3d-b394-8e51f23833c4", + "numCpu": 8, + "runtimeVersion": "go1.9", + "version": "dev-master" + }, + "type": "identify", + "userId": "22b137b6aae9bc40feb7ff14a08a1b9ecbc6305f77956214404c5b744c3b3fe2", + "writeKey": "yF6PTASTliRjCtRbUnwgsvjrvneqACDM", + "sentAt": "2018-01-18T18:41:42.546Z", + "integrations": {}, + "receivedAt": "2018-01-18T18:41:41.972Z", + "originalTimestamp": "2018-01-18T19:41:37.6027834+01:00" + }, + { + "context": { + "ip": "0.0.0.0", + "library": { + "name": "analytics-go", + "version": "3.0.0" + } + }, + "messageId": "258f0127-498a-4d71-8c55-ce678a5d92b8", + "name": "/clients", + "properties": { + "latency": 0, + "method": "GET", + "name": "/clients", + "path": "/clients", + "size": 154, + "status": 401, + "url": "http://22b137b6aae9bc40feb7ff14a08a1b9ecbc6305f77956214404c5b744c3b3fe2/clients" + }, + "timestamp": "2018-01-18T18:41:49.537Z", + "type": "page", + "userId": "22b137b6aae9bc40feb7ff14a08a1b9ecbc6305f77956214404c5b744c3b3fe2", + "writeKey": "yF6PTASTliRjCtRbUnwgsvjrvneqACDM", + "sentAt": "2018-01-18T18:41:52.547Z", + "integrations": {}, + "receivedAt": "2018-01-18T18:41:51.380Z", + "originalTimestamp": "2018-01-18T19:41:50.7046198+01:00" + } +] +``` diff --git a/docs/ecosystem/sustainability.md b/docs/ecosystem/sustainability.md new file mode 100644 index 000000000..19570ec1d --- /dev/null +++ b/docs/ecosystem/sustainability.md @@ -0,0 +1,9 @@ +--- +id: ecosystem-sustainability +title: Sustainability +--- + +ORY builds a sustainable infrastructure. To guarantee sustainability, the following aspects are core principles: + +1. Heavily engage with the open source community. +2. Open source projects do not diverge from their monetized versions. diff --git a/docs/ecosystem/upgrading.md b/docs/ecosystem/upgrading.md new file mode 100644 index 000000000..8738da19d --- /dev/null +++ b/docs/ecosystem/upgrading.md @@ -0,0 +1,18 @@ +--- +id: ecosystem-upgrading +title: Software Updates +--- + +Good software improves over time. If it wouldn't, you shouldn't use it. Unfortunately, some of these improvements +have breaking changes. We know that breaking changes are annoying so we want to make upgrading as painless as possible. + +We document detailed changelogs and upgrade guides for this very reason: + +* ORY Hydra: [upgrade guide](https://github.com/ory/hydra/blob/master/UPGRADE.md), [changelog](https://github.com/ory/hydra/blob/master/CHANGELOG.md) +* ORY Oathkeeper: [upgrade guide](https://github.com/ory/oathkeeper/blob/master/UPGRADE.md), [changelog](https://github.com/ory/oathkeeper/blob/master/CHANGELOG.md) +* ORY Keto: [upgrade guide](https://github.com/ory/keto/blob/master/UPGRADE.md), [changelog](https://github.com/ory/keto/blob/master/CHANGELOG.md) + +Before upgrading to a newer version, please make sure to check with these documents first. + +**If you have not already subscribed to our release announcements, [subscribe now](http://eepurl.com/di390P)!** We announce +important releases (e.g. security releases) in this newsletter. diff --git a/docs/ecosystem/versioning.md b/docs/ecosystem/versioning.md new file mode 100644 index 000000000..a2227d97a --- /dev/null +++ b/docs/ecosystem/versioning.md @@ -0,0 +1,243 @@ +--- +id: ecosystem-versioning +title: Software Versions +--- + +The ORY ecosystem consists of multiple services versioned using [semantic versioning](https://semver.org). This section +explains how we define service versions and what they mean. + +There are two types of services: + +- **Graduated services** change rarely in a backwards incompatible fashion. A service can be considered graduated if +the major version is >= 1 - for example: `v1.0.1`, `v2.2.2`. If a serious backwards incompatible +change is made, the major version jumps one up. Most, if not all, REST APIs will provide backwards compatible transformations that +make it possible to interact with the server using older API concepts. +- **Incubating services** implement well defined concepts but do not provide backwards compatible REST APIs yet. Incubating +services are indicated by major version numbers of `0` - for example: `v0.10.0`. There is a very low risk of breaking +backwards compatibility with regards to concepts and there will always be upgrade guides. +- **Sandbox services** may implement concepts, provide APIs and CLIs which are not fully baked yet. It is possible that +these services change in unpredictable ways. These services are indicated by major version numbers of `0` and the +`sandbox` label - for example: `v0.10.0-sandbox`. We will provide upgrade guides wherever possible and when adopters +rely in production on that particular service. + +To make deployment easier but stay compatible with semantic versioning, each service is equipped with a `oryOS` version +number denoted by `+oryOS.X` where `X` represents the "ecosystem" version. This is especially useful if you use more +than one service from the ORY ecosystem. Let's look at some examples: + +- ORY Hydra `v1.0.1+oryOS.6` is best compatible with ORY Oathkeeper `v0.13.1+oryOS.6` and ORY Keto `v0.5.1-sandbox+oryOS.6`. +- ORY Hydra `v1.0.2+oryOS.7` is best compatible with ORY Oathkeeper `v0.13.2+oryOS.7` and ORY Keto `v0.6.0-sandbox+oryOS.7`. +- ORY Hydra `v1.0.3+oryOS.8` is probably not fully compatible with ORY Oathkeeper `v0.13.1+oryOS.6` nor with ORY Keto +`v0.5.1-sandbox+oryOS.6`. +- ORY Hydra `v1.1.0+oryOS.9` is best compatible with ORY Oathkeeper `v1.1.0+oryOS.9` and ORY Keto `v1.1.0+oryOS.9`. + +It is possible that a number of releases are versioned with the same `oryOS.x` version metadata: + +- ORY Hdyra `v1.0.1+oryOS.10`, `v1.0.2+oryOS.10` is guaranteed compatible with ORY Oathkeeper `v0.15.5+oryOS.10`, `v1.16.0+oryOS.10` +and ORY Keto `v0.1.1+oryOS.10`, `v0.1.2+oryOS.10`. + +> Each release - unless explicitly labeled as `-unstable` - is going through extensive quality assurance +and is considered secure and reliable enough to be run in production. If you choose to go with an incubating or sandbox +service, it is likely that you will spend some time addressing breaking changes. + +We always provide ways to migrate breaking changes, and all breaking changes are meticulously described in each project's +`UPGRADE.md` as well as `HISTORY.md`. + +## Compatibility + +This compatibility matrix shows which versions are compatible with one another. + +### Documentation + +| Software version | ORY Hydra | ORY Keto | ORY Oathkeeper | +| ---------------- | ------------------------------------ | ------------------------------------ | ------------------------------ | +| `oryOS.9` | `v1.0.0-beta.9` | `v0.1.9-sandbox+oryOS.9` | `v0.13.9+oryOS.9` | + +### Docker Hub and NPM + +[Docker doryOS not allow `+` in version tags](https://github.com/moby/moby/issues/16304) and +[Docker is not fully semver compatible](https://github.com/docker/distribution/pull/1202#issuecomment-161075954). + +As we still want to provide you with a way of checking if you have the right docker image, the `+` symbol will be +replaced by `_`. For example, ORY Hydra `v1.2.3+oryOS.123` is available on Docker as `v1.2.3_oryOS.123`. + +NPM also doryOS not support `+` in version tags. Since npm modules are only required for interacting with the service +itself, `+oryOS` is not available in npm packages at all. + +| Example version | Docker Hub | NPM | +| ---------------------- | -------------------------------------- | ----------------- | +| `v1.2.3-rc1+oryOS.123` | `v1.2.3-rc1_oryOS.123` | `v1.2.3-rc1` | + +### Service compatibility matrix + +| oryOS | ORY Hydra | ORY Keto | ORY Oathkeeper | +| ------------- | ------------------------ | ------------------------ | ---------------------- | +| `oryOS.9` | `v1.0.0-beta.9` | `v0.1.9-sandbox+oryOS.9` | `v0.13.9+oryOS.9` | +| `oryOS.8` | `v1.0.0-beta.8` | `v0.1.8-sandbox+oryOS.8` | `v0.13.8+oryOS.8` | +| `oryOS.7` | `v1.0.0-beta.7` | `v0.1.7-sandbox+oryOS.7` | `v0.13.7+oryOS.7` | +| `oryOS.6` | `v1.0.0-beta.6` | `v0.1.6-sandbox+oryOS.6` | `v0.13.6+oryOS.6` | +| `oryOS.5` | `v1.0.0-beta.5` | `v0.1.5-sandbox+oryOS.5` | `v0.13.5+oryOS.5` | +| `oryOS.4` | `v1.0.0-beta.4` | `v0.1.4-sandbox+oryOS.4` | `v0.13.4+oryOS.4` | +| `oryOS.3` | `v1.0.0-beta.3` | `v0.1.3-sandbox+oryOS.3` | `v0.13.3+oryOS.3` | +| `oryOS.2` | `v1.0.0-beta.2` | `v0.1.2-sandbox+oryOS.2` | `v0.13.2+oryOS.2` | +| `oryOS.1` | `v1.0.0-beta.1` | `v0.1.1-sandbox+oryOS.1` | `v0.13.1+oryOS.1` | + +## November 2018 Change + +If you are an adopter of the ORY Keto ory ORY Oathkeeper prior to November 2018, this +section is for you. + +### Why + +Before November 2018, ORY Keto and ORY Oathkeeper were available as versions `v1.0.0-beta.1` through `v1.0.0-beta.9`. +We wanted to make versioning as easy as possible across all services. For example, if you ran ORY Hydra `v1.2.3` and ORY Oathkeeper +`v1.2.3` both versions would be guaranteed compatible. + +This turned out to be a bad decision because of multiple factors: + +1. As ORY Hydra was moving towards `v1.0.0`, ORY Oathkeeper and ORY Keto were still rather new projects and some of them +required breaking changes to improve the value of the technology. But semantic versioning, which is a de-facto standard, +has strong implications on breaking changes in software labeled as `v1.x.y`. While we had +plans to add tags like `-sandbox` to software, labeled as `v1` but not fully matured, we quickly realized that this would +only add to confusion. +2. While certain services matured rather quickly and the community was eagerly awaiting the next release, we had to hold +back because of unfinished changes to other projects. ORY Hydra `v1.0.0-rc.1` was put on delay because we +wanted other systems to feel `v1`-ish, which required a lot of work, stress and caused delays. This lead to a misalignment +between what the community wants (ORY Hydra rc1) and what had to be added to the roadmap in order to comply with the +versioning concept. +3. We are working towards an open source identity management system (ORY Hive) which will stay in sandbox and incubation +for a while due to its complexity. We definitely did not want to give the impression that early MVPs are `v1` or even `v2`. + +Our plan to help developers understand which versions are compatible with one another backfired and instead we delayed +releases and developers were confused as to the maturity of the individual services. + +### Upgrading + +The new versioning framework was published in November 2018 at the top of this page. This is an overview of the things that +changed. + +#### ORY Hydra + +No changes to the versioning will be made. The next release will remain `v1.0.0-rc.1+oryOS.10`. + +Going forward, the npm package `ory-hydra-sdk` will no longer be continued. Instead, new versions will be pushed to +`@oryd/hydra`. Version `v1.0.0-beta.9` will be the last available version at `ory-hydra-sdk`. Version `v1.0.0-rc.1+oryOS.10` +will be the first version available at `@oryd/hydra`. + +##### Version Matrix + +| Old version | Re-released as | NPM | Docker Hub | +| --------------- | --------------------------------- | ----------------------------- | --------------------------------- | +| `v1.0.0-beta.9` | - | `ory-hydra-sdk:v1.0.0-beta.9` | `oryd/hydra:v1.0.0-beta.9` | +| - | `v1.0.0-rc.1+oryOS.10` (example) | `@oryd/hydra:v1.0.0-rc.1` | `oryd/hydra:v1.0.0-rc.1_oryOS.10` | + +#### ORY Oathkeeper + +ORY Oathkeeper is a service in incubation phase. To comply with the new versioning framework, +the following changes will be made: + +* [GitHub Releases](https://github.com/ory/oathkeeper/releases) `v1.0.0-beta.1` to `v1.0.0-beta.9` +have been removed as well as their binaries uploaded to GitHub Releases. +Releases `v1.0.0-beta.1` to `v1.0.0-beta.9`, including binaries, have been re-released as +`v0.13.1+oryOS.1` to `v0.13.9+oryOS.9`. Binaries, code, and functionality for versions `v1.0.0-beta.2+oryOS.2` and `v0.13.2+oryOS.2` are +equal. +* [Git Tags](https://github.com/ory/oathkeeper/tags) will be removed on **Jan 15th 2019**. Until then, you will +be able to check out the specific commits using e.g. `git checkout v1.0.0-beta.2 -b my-branch`. Tags `v1.0.0-beta.1` +to `v1.0.0-beta.9` will be re-released as `v0.13.1+oryOS.1` to `v0.13.9+oryOS.2`. After Jan 15th 2019 you will be able +to get the same functionality as before, but you have to change the tag from `v1.0.0-beta.x` to `v0.13.x+oryOS.x`. +* Versions `v1.0.0-beta.1` to `v1.0.0-beta.9` will be removed from +[Docker Hub `oryd/oathkeeper`](https://hub.docker.com/r/oryd/oathkeeper/) on **Jan 15th 2019**. These have been +re-released according to the same rules that apply for Git Tags, but with `_oryOS.x` instead of `+oryOS.x`. Image `oryd/oathkeeper:v1.0.0-beta.2` will +thus be available as `oryd/oathkeeper:v0.13.2_oryOS.2`. +* The npm package [`ory-oathkeeper-sdk`](https://www.npmjs.com/package/ory-oathkeeper-sdk) will be deprecated. The new +npm package will be available at `@oryd/oathkeeper`. The new npm package will only serve versions under the new +format `v0.13.x+oryOS.x`. + +##### Version Matrix + +| Old version | Re-released as | NPM | Docker Hub | +| --------------- | ----------------------------- | ----------------------------- | ------------------------------ | +| `v1.0.0-beta.1` | `v0.13.1+oryOS.1` | `@oryd/oathkeeper:v0.13.1`, `ory-oathkeeper-sdk:v1.0.0-beta.1` (deprecated!) | `oryd/oathkeeper:v0.13.1_oryOS.1`, `oryd/oathkeeper:v1.0.0-beta.1` (deleted Jan 15th 2019) | +| `v1.0.0-beta.2` | `v0.13.2+oryOS.2` | `@oryd/oathkeeper:v0.13.2`, `ory-oathkeeper-sdk:v1.0.0-beta.2` (deprecated!) | `oryd/oathkeeper:v0.13.2_oryOS.2`, `oryd/oathkeeper:v1.0.0-beta.2` (deleted Jan 15th 2019) | +| `v1.0.0-beta.3` | `v0.13.3+oryOS.3` | `@oryd/oathkeeper:v0.13.3`, `ory-oathkeeper-sdk:v1.0.0-beta.3` (deprecated!) | `oryd/oathkeeper:v0.13.3_oryOS.3`, `oryd/oathkeeper:v1.0.0-beta.3` (deleted Jan 15th 2019) | +| `v1.0.0-beta.4` | `v0.13.4+oryOS.4` | `@oryd/oathkeeper:v0.13.4`, `ory-oathkeeper-sdk:v1.0.0-beta.4` (deprecated!) | `oryd/oathkeeper:v0.13.4_oryOS.4`, `oryd/oathkeeper:v1.0.0-beta.4` (deleted Jan 15th 2019) | +| `v1.0.0-beta.5` | `v0.13.5+oryOS.5` | `@oryd/oathkeeper:v0.13.5`, `ory-oathkeeper-sdk:v1.0.0-beta.5` (deprecated!) | `oryd/oathkeeper:v0.13.5_oryOS.5`, `oryd/oathkeeper:v1.0.0-beta.5` (deleted Jan 15th 2019) | +| `v1.0.0-beta.6` | `v0.13.6+oryOS.6` | `@oryd/oathkeeper:v0.13.6`, `ory-oathkeeper-sdk:v1.0.0-beta.6` (deprecated!) | `oryd/oathkeeper:v0.13.6_oryOS.6`, `oryd/oathkeeper:v1.0.0-beta.6` (deleted Jan 15th 2019) | +| `v1.0.0-beta.7` | `v0.13.7+oryOS.7` | `@oryd/oathkeeper:v0.13.7`, `ory-oathkeeper-sdk:v1.0.0-beta.7` (deprecated!) | `oryd/oathkeeper:v0.13.7_oryOS.7`, `oryd/oathkeeper:v1.0.0-beta.7` (deleted Jan 15th 2019) | +| `v1.0.0-beta.8` | `v0.13.8+oryOS.8` | `@oryd/oathkeeper:v0.13.8`, `ory-oathkeeper-sdk:v1.0.0-beta.8` (deprecated!) | `oryd/oathkeeper:v0.13.8_oryOS.8`, `oryd/oathkeeper:v1.0.0-beta.8` (deleted Jan 15th 2019) | +| `v1.0.0-beta.9` | `v0.13.9+oryOS.9` | `@oryd/oathkeeper:v0.13.9`, `ory-oathkeeper-sdk:v1.0.0-beta.9` (deprecated!) | `oryd/oathkeeper:v0.13.9_oryOS.9`, `oryd/oathkeeper:v1.0.0-beta.9` (deleted Jan 15th 2019) | +| - | `v0.13.10+oryOS.10` (example) | `@oryd/oathkeeper:v0.13.10` | `oryd/oathkeeper:v0.13.10_oryOS.10` | + +> No code or database changes will be required when moving from `v1.0.0-beta.x` to `v0.13.x+oryOS.x` + +#### ORY Keto + +ORY Keto is a service in sandbox phase. To comply with the new versioning rules, the following changes will be made: + +* [GitHub Releases](https://github.com/ory/keto/releases) `v1.0.0-beta.1` to `v1.0.0-beta.9` +have been removed as well as their binaries uploaded to GitHub Releases. +Releases `v1.0.0-beta.1` to `v1.0.0-beta.9`, including binaries, have been re-released as +`v0.1.1-sandbox+oryOS.1` to `v0.1.9-sandbox+oryOS.9`. Binaries, code, and functionality for versions `v1.0.0-beta.2` and `v0.1.2-sandbox+oryOS.2` are +equal. +* [Git Tags](https://github.com/ory/keto/tags) will be removed on **Jan 15th 2019**. Until then, you will +be able to check out the specific commits using e.g. `git checkout v1.0.0-beta.2 -b my-branch`. Tags `v1.0.0-beta.1` +to `v1.0.0-beta.9` will be re-released as `v0.1.1-sandbox+oryOS.1` to `v0.1.9-sandbox+oryOS.9`. After Jan 15th 2019 you will be able +to get the same functionality as before, but you have to change the tag from `v1.0.0-beta.x` to `v0.1.x-sandbox+oryOS.x`. +* Versions `v1.0.0-beta.1` to `v1.0.0-beta.9` will be removed from +[Docker Hub `oryd/keto`](https://hub.docker.com/r/oryd/keto/) on **Jan 15th 2019**. These have been +re-released according to the same rules that apply for Git Tags. Image `oryd/keto:v1.0.0-beta.2` will +thus be available as `oryd/keto:v0.1.2-sandbox_oryOS.2`. +* The npm package [`ory-keto-sdk`](https://www.npmjs.com/package/ory-keto-sdk) will be deprecated. The new +npm package will be available at `@oryd/keto`. The new npm package will only serve versions under the new +format `v0.1.x-sandbox+oryOS.x`. + +##### Version Matrix + +| Old version | Re-released as | NPM | Docker Hub | +| --------------- | ------------------------------------- | ----------------------------- | ------------------------------ | +| `v1.0.0-beta.1` | `v0.1.1-sandbox+oryOS.1` | `@oryd/keto:v0.1.1-sandbox`, `ory-keto-sdk:v1.0.0-beta.1` (deprecated!) | `oryd/keto:v0.1.1-sandbox_oryOS.1`, `oryd/keto:v1.0.0-beta.1` (deleted Jan 15th 2019) | +| `v1.0.0-beta.2` | `v0.1.2-sandbox+oryOS.2` | `@oryd/keto:v0.1.2-sandbox`, `ory-keto-sdk:v1.0.0-beta.2` (deprecated!) | `oryd/keto:v0.1.2-sandbox_oryOS.2`, `oryd/keto:v1.0.0-beta.2` (deleted Jan 15th 2019) | +| `v1.0.0-beta.3` | `v0.1.3-sandbox+oryOS.3` | `@oryd/keto:v0.1.3-sandbox`, `ory-keto-sdk:v1.0.0-beta.3` (deprecated!) | `oryd/keto:v0.1.3-sandbox_oryOS.3`, `oryd/keto:v1.0.0-beta.3` (deleted Jan 15th 2019) | +| `v1.0.0-beta.4` | `v0.1.4-sandbox+oryOS.4` | `@oryd/keto:v0.1.4-sandbox`, `ory-keto-sdk:v1.0.0-beta.4` (deprecated!) | `oryd/keto:v0.1.4-sandbox_oryOS.4`, `oryd/keto:v1.0.0-beta.4` (deleted Jan 15th 2019) | +| `v1.0.0-beta.5` | `v0.1.5-sandbox+oryOS.5` | `@oryd/keto:v0.1.5-sandbox`, `ory-keto-sdk:v1.0.0-beta.5` (deprecated!) | `oryd/keto:v0.1.5-sandbox_oryOS.5`, `oryd/keto:v1.0.0-beta.5` (deleted Jan 15th 2019) | +| `v1.0.0-beta.6` | `v0.1.6-sandbox+oryOS.6` | `@oryd/keto:v0.1.6-sandbox`, `ory-keto-sdk:v1.0.0-beta.6` (deprecated!) | `oryd/keto:v0.1.6-sandbox_oryOS.6`, `oryd/keto:v1.0.0-beta.6` (deleted Jan 15th 2019) | +| `v1.0.0-beta.7` | `v0.1.7-sandbox+oryOS.7` | `@oryd/keto:v0.1.7-sandbox`, `ory-keto-sdk:v1.0.0-beta.7` (deprecated!) | `oryd/keto:v0.1.7-sandbox_oryOS.7`, `oryd/keto:v1.0.0-beta.7` (deleted Jan 15th 2019) | +| `v1.0.0-beta.8` | `v0.1.8-sandbox+oryOS.8` | `@oryd/keto:v0.1.8-sandbox`, `ory-keto-sdk:v1.0.0-beta.8` (deprecated!) | `oryd/keto:v0.1.8-sandbox_oryOS.8`, `oryd/keto:v1.0.0-beta.8` (deleted Jan 15th 2019) | +| `v1.0.0-beta.9` | `v0.1.9-sandbox+oryOS.9` | `@oryd/keto:v0.1.9-sandbox`, `ory-keto-sdk:v1.0.0-beta.9` (deprecated!) | `oryd/keto:v0.1.9-sandbox_oryOS.9`, `oryd/keto:v1.0.0-beta.9` (deleted Jan 15th 2019) | +| - | `v0.1.10-sandbox+oryOS.10` (example) | `@oryd/keto:v0.1.10-sandbox` | `oryd/keto:v0.1.10-sandbox_oryOS.10` | + +> No code or database changes will be required when moving from `v1.0.0-beta.x` to `v0.1.x-sandbox` + +#### ORY Docs + +The documentation got a major overhaul. We completely deprecated GitBook, which is no longer being +developed, and moved to [Docusaurus](https://docusaurus.io/). Additionally, the API documentation is +now integrated in Docusaurus using [widdershins](https://github.com/Mermade/widdershins) with custom templates, and no +longer uses SwaggerUI. This is great, because everything is now in one place - and we added code examples for consuming +the REST API for all major programming languages! + +The new documentation will serve documentation the latest version (new version semantics) only, this will be `oryOS.9`. +This means that the following versions will be documented in the new docs: + +* ORY Hydra `v1.0.0-beta.9` +* ORY Oathkeeper `v0.13.9+oryOS.9` +* ORY Keto `v0.1.9-sandbox+oryOS.9` + +If you need to look up documentation on older versions (in the old format), you will be able to do so +[here](https://www.ory.sh/docs/deprecated). This documentation will be available for a reasonable amount of time and you will receive +a notice with ample time if we shut that down. You can view the legacy REST API Documentation by browsing to the +[hosted swagger ui](https://petstore.swagger.io/) and entering, for example, +`https://raw.githubusercontent.com/ory/keto/v1.0.0-beta.8/docs/api.swagger.json` in the top bar. + +#### ORY Security Console + +The ORY Security Console will be compatible with `oryOS.9`. While we work on implementing the new changes there you might +see error messages regarding compatibility. You may ignore them for now. + +#### Outlook + +Versions `v1.0.0-beta.1` to `v1.0.0-beta.9` are blacklisted for ORY Oathkeeper and ORY Keto. No future release +will have those versions. This will prevent potential future conflicts. + +#### Conflict Resolution + +If you need help with these changes or want us to handle these updates for you, [write us now](mailto:hi@ory.sh]. diff --git a/docs/hydra/5min-tutorial.md b/docs/hydra/5min-tutorial.md new file mode 100644 index 000000000..975d181d4 --- /dev/null +++ b/docs/hydra/5min-tutorial.md @@ -0,0 +1,136 @@ +--- +id: hydra-5min-tutorial +title: 5 Minute Tutorial +--- + +To get started quickly, we provide a Docker Compose based example for setting up ORY Hydra, a PostgreSQL instance +and an exemplary user login & consent app. You need to have the latest Docker as well as Docker Compose version installed. + +OAuth2 Flow + +Next, clone (`git clone https://github.com/ory/hydra.git`), [download](https://github.com/ory-am/hydra/archive/master.zip), +or use `go get -d github.com/ory/hydra` - if you have Go (1.10+) installed on you system - to download the Docker Compose +set up. + +``` +$ git clone https://github.com/ory/hydra.git +$ cd hydra +$ git checkout tags/v1.0.0-beta.8 + +$ docker-compose -p hydra up --build +Starting hydra_mysqld_1 +Starting hydra_postgresd_1 +Starting hydra_hydra_1 + +[...] +``` + + +Everything should running now! Let's confirm that everything is working by creating our first OAuth 2.0 Client. +The following commands will use Docker wizardry. You can obviously install the ORY Hydra CLI locally and avoid using +Docker here. If you do use the CLI locally, you can omit `docker exec -it hydra_hydra_1 \` completely. + +You will notice that two ports are being used. Port `4444` and port `4445`. The former is for request to ORY Hydra's public +endpoints. The latter to its administrative endpoints. For more information on this, head over to +[Exposing Administrative and Public API Endpoints](production.md). If you want to run ORY Hydra admin and +public services in two separate containers, run + +``` +$ docker-compose -p hydra -f docker-compose-twoc.yml up --build +``` + +Please be aware that you will not be able to run the hydra CLI from within docker if you +use the docker-compose-twoc.yml file. Instead, you must install the CLI locally and +omit `docker exec -it hydra_hydra_1 \` from your commands. + +Ok, let's continue by creating a new OAuth 2.0 Client. + +``` +# Creates a new OAuth 2.0 client +$ docker exec -it hydra_hydra_1 \ + hydra clients create \ + --endpoint http://localhost:4445 \ + --id my-client \ + --secret secret \ + -g client_credentials + +OAuth2 client id: hydra-my-client +OAuth2 client secret: secret + +# Let's perform the client credentials grant. +$ docker exec -it hydra_hydra_1 \ + hydra token client \ + --endpoint http://localhost:4444 \ + --client-id my-client \ + --client-secret secret + +UDYMha9TwsMBejEvKfnDOXkhgkLsnmUNYVQDklT5bD8.ZNpuNRC85erbIYDjPqhMwTinlvQmNTk_UvttcLQxFJY + +# Let's perform token introspection on that token. Make sure to copy the token you just got and not the dummy value. +$ docker exec -it hydra_hydra_1 \ + hydra token introspect \ + --endpoint http://localhost:4445 \ + --client-id my-client \ + --client-secret secret \ + UDYMha9TwsMBejEvKfnDOXkhgkLsnmUNYVQDklT5bD8.ZNpuNRC85erbIYDjPqhMwTinlvQmNTk_UvttcLQxFJY + +{ + "active": true, + "client_id": "my-client", + "exp": 1527078658, + "iat": 1527075058, + "iss": "http://localhost:4444", + "sub": "my-client", + "token_type": "access_token" +} +``` + +Next, we will perform the OAuth 2.0 Authorization Code Grant. For that, we must first create a client that is capable +of performing that grant: + +``` +$ docker exec -it hydra_hydra_1 \ + hydra clients create \ + --endpoint http://localhost:4445 \ + --id auth-code-client \ + --secret secret \ + --grant-types authorization_code,refresh_token \ + --response-types code,id_token \ + --scope openid,offline \ + --callbacks http://127.0.0.1:5555/callback +``` + +Note that you need to add `--token-endpoint-auth-method none` if your clients are public (such as SPA apps and native apps) because the public clients could not provide client secret. + + +The next command starts a server that serves an example web application. The application will perform the OAuth 2.0 +Authorization Code Flow using ORY Hydra. The web server runs on [http://127.0.0.1:5555](http://127.0.0.1:5555). + +``` +$ docker exec -it hydra_hydra_1 \ + hydra token user \ + --client-id auth-code-client \ + --client-secret secret \ + --endpoint http://localhost:4444/ \ + --port 5555 \ + --scope openid,offline + +Setting up home route on http://127.0.0.1:4445/ +Setting up callback listener on http://127.0.0.1:4445/callback +Press ctrl + c on Linux / Windows or cmd + c on OSX to end the process. +If your browser does not open automatically, navigate to: + + http://127.0.0.1:5555/ +``` + +Open the URL [http://127.0.0.1:5555/](http://127.0.0.1:5555/), log in, and authorize the application. Next, you should +see at least an access token in the response. If you granted the `offline` scope, you will also see a refresh token. +If you granted the `openid` scope, you will get an ID Token as well. + +Great! You installed hydra, connected the CLI, created a client and completed two authentication flows! +Before you continue, clean up this set up in order to avoid conflicts with other tutorials form this guide: + +``` +$ docker-compose kill +$ docker-compose rm -f +``` diff --git a/docs/hydra/advanced.md b/docs/hydra/advanced.md new file mode 100644 index 000000000..47d90e180 --- /dev/null +++ b/docs/hydra/advanced.md @@ -0,0 +1,282 @@ +--- +id: hydra-advanced +title: Advanced Topics +--- + +This guide aims to help setting up a production system with ORY Hydra. + + + +## Mobile & Browser (SPA) Authorization + +We have an [excellent blog post](https://www.ory.sh/oauth2-for-mobile-app-spa-browser) on this topic. Read it now! + +### Creating a public OAuth 2.0 Client + +You can create a public OAuth 2.0 Client (e.g. for the authorize code + PKCE or implicit flow) with the CLI + +``` +hydra clients create --endpoint http://ory-hydra-admin-api --token-endpoint-auth-method none +``` + +or by setting in the HTTP API JSON body when POSTing to `/clients`: + +``` +{ + "client_id": "...", + "token_endpoint_auth_method": "none" +} +``` + +Be aware that when making requests to `/oauth2/token` with a public OAuth 2.0 Client, you can not authenticate with the +HTTP Basic Authorization but must include the `client_id` in the POST body. + +## Key rotation + +Key rotation is simple with ORY Hydra. You can rotate OpenID Connect ID Token and OAuth 2.0 Access Tokens (JSON Web Token) +keys with one simple command. + +ORY Hydra takes the latest key from the key store to sign JSON Web Tokens. All public keys will be shown at +`http://ory-hydra-public-api/.well-known/jwks.json`. + +### OpenID Connect ID Token + +``` +hydra keys create --endpoint=http://ory-hydra-admin-api/ hydra.openid.id-token -a RS256 +``` + +### OAuth 2.0 Access Tokens (JSON Web Token) + +``` +hydra keys create --endpoint=http://ory-hydra-admin-api/ hydra.jwt.access-token -a RS256 +``` + +## OAuth 2.0 + +### Audience + +There are two types of audience concept in the context of OAuth 2.0 and OpenID Connect: + +1. OAuth 2.0: Access and Refresh Tokens are "internal-facing". The `aud` claim of an OAuth 2.0 Access and Refresh token +defines at which *endpoints* the token can be used. +2. OpenID Connect: The ID Token is "external-facing". The `aud` claim of an OpenID Connect ID Token defines which +*clients* should accept it. + +While modifying the audience of an ID Token is not desirable, specifying the audience of an OAuth 2.0 Access Token is. +This is not defined as an IETF Standard but is considered good practice in certain environments. + +For this reason, Hydra allows you to control the aud claim of the access token. To do so, you must specify the intended +audiences in the OAuth 2.0 Client's metadata on a per-client basis: + + +``` +{ + "client_id": "...", + "audience": ["https://api.my-cloud.com/user", "https://some-tenant.my-cloud.com/"] +} +``` + +The audience is a list of case-sensitive URLs. **URLs must not contain whitespaces**. + +#### OAuth 2.0 Authorization Code, Implicit, Hybrid Flows + +When performing an OAuth 2.0 authorize code, implicit, or hybrid flow, you can request audiences at the `/oauth2/auth` +endpoint `https://my-hydra.com/oauth2/auth?client_id=...&scope=...&audience=https%3A%2F%2Fapi.my-cloud.com%2Fuser+https%3A%2F%2Fsome-tenant.my-cloud.com%2F` +which requests audiences `https://api.my-cloud.com/user` and `https://some-tenant.my-cloud.com/`. + +The `audience` query parameter may contain multiple strings separated by a url-encoded space (`+` or `%20`). The +audience values themselves must also be url encoded. The values will be validated against the whitelisted +audiences defined in the OAuth 2.0 Client: + +* An OAuth 2.0 Client with the allowed audience `https://api.my-cloud/user` is allowed to request audience values `https://api.my-cloud/user` +`https://api.my-cloud/user/1234` but not `https://api.my-cloud/not-user` nor `https://something-else/`. + +The requested audience from the query parameter is then part of the login and consent request payload as field `requested_access_token_audience`. +You can then alter the audience using `grant_audience.access_token` when accepting the consent request: + +``` +hydra.acceptConsentRequest(challenge, { + // ORY Hydra checks if requested audiences are allowed by the client, so we can simply echo this. + grant_audience: { + access_token: response.requested_access_token_audience, + // or, for example: + // access_token: ["https://api.my-cloud/not-user"] + }, + + // ... remember: false + // ... +}) +``` + +When introspecting the OAuth 2.0 Access Token, the response payload will include the audience: + +``` +{ + "active": true, + // ... + "audience": ["https://api.my-cloud/user", "https://api.my-cloud/user/1234"] +} +``` + +#### OAuth 2.0 Client Credentials Grant + +When performing the client credentials grant, the audience parameter from the POST body of the `/oauth2/token` is +decoded and validated according to the same rules of the previous section, except for the login and consent part which +does not exist for this flow. + +### JSON Web Tokens + +ORY Hydra supports issuing OAuth 2.0 Access Tokens as JSON Web Tokens. Using JSON Web Tokens as Access Tokens is **a bad idea and nobody +serious, including Google, uses them in this place**. JSON Web Tokens are obviously not bad per se, but in the context +of OAuth 2.0 Access Tokens they are an inferior tool for the job. Here are all the reasons why we *discourage you from +using this feature:* + +1. OAuth 2.0 Access Tokens are "internal". They (often) contain internal/private session data such as a user's email +or contact address, subscription status, and other potentially sensitive information. The user (often) did not +consent to giving out this data and the OpenID Connect ID Token, which explicitly governs access to this data, should +be the only place where OAuth 2.0 Clients get access to this information. +2. OAuth 2.0 Access Tokens may contain information on the permission or access control system. This information +should be treated as confidential information. Exposing it gives attackers one more source of knowledge about your system. +3. Using this feature disables other features, like the pairwise Subject Identifier Algorithm. +4. This feature is new and has not been battle-tested. + +If you are looking for stateless authorization at your APIs, that is a valid use case. Our recommendation however is +to rely on opaque OAuth 2.0 Access Tokens and convert them to JSON Web Tokens at your API Gateway, for example by +using [ORY Oathkeeper](https://github.com/ory/oathkeeper). + +If you still want to use this strategy despite all these warnings, +you can do so by setting environment variable `OAUTH2_ACCESS_TOKEN_STRATEGY=jwt`. + +Be aware that only access tokens are formatted as JSON Web Tokens. Refresh tokens are not impacted by this strategy. +By performing OAuth 2.0 Token Introspection you can check if the token is still valid. If a token is revoked or otherwise +blacklisted, the OAuth 2.0 Token Introspection will return `{ "active": false }`. This is useful when you do not want +to rely only on the token's expiry. + +#### JSON Web Token Validation + +You can validate JSON Web Tokens issued by ORY Hydra by pointing your `jwt` library (e.g. [node-jwks-rsa](https://github.com/auth0/node-jwks-rsa)) +to `http://ory-hydra-public-api/.well-known/jwks.json`. All necessary keys are available there. + +### OAuth 2.0 Client Authentication with RSA private/public keypairs + +ORY Hydra supports OAuth 2.0 Client Authentication with RSA private/public keypairs. This authentication method +replaces the classic HTTP Basic Authorization and HTTP POST Authorization schemes. Instead of sending the `client_id` +and `client_secret`, you authenticate the client with a signed JSON Web Token. + +To enable this feature for a specific OAuth 2.0 Client, you must set `token_endpoint_auth_method` to `private_key_jwt` +and register the public key of the RSA signing key either using the `jwks_uri` or `jwks` fields of the client. + +When authenticating the client at the token endpoint, you generate and sign (with the RSA private key) a JSON Web Token +with the following claims: + +* `iss`: REQUIRED. Issuer. This MUST contain the client_id of the OAuth Client. +* `sub`: REQUIRED. Subject. This MUST contain the client_id of the OAuth Client. +* `aud`: REQUIRED. Audience. The aud (audience) Claim. Value that identifies the Authorization Server (ORY Hydra) as an +intended audience. The Authorization Server MUST verify that it is an intended audience for the token. +The Audience SHOULD be the URL of the Authorization Server's Token Endpoint. +* `jti`: REQUIRED. JWT ID. A unique identifier for the token, which can be used to prevent reuse of the token. +These tokens MUST only be used once, unless conditions for reuse were negotiated between the parties; any such +negotiation is beyond the scope of this specification. +* `exp`: REQUIRED. Expiration time on or after which the ID Token MUST NOT be accepted for processing. +* `iat`: OPTIONAL. Time at which the JWT was issued. + +When making a request to the `/oauth2/token` endpoint, you include `client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer` +and `client_assertion=` in the request body: + +``` +POST /oauth2/token HTTP/1.1 +Host: my-hydra.com +Content-Type: application/x-www-form-urlencoded + +grant_type=authorization_code& +code=i1WsRn1uB1& +client_id=s6BhdRkqt3& +client_assertion_type= +urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer& +client_assertion=PHNhbWxwOl ... ZT +``` + +Here's what a client with a `jwks` looks like: + +``` +{ + "client_id": "client-jwks", + "jwks": { + "keys": [ + { + "kty": "RSA", + "n": "jL7h5wc-yeMUsHGJHc0xe9SbTdaLKXMHvcIHQck20Ji7SvrHPdTDQTvZtTDS_wJYbeShcCrliHvbJRSZhtEe0mPJpyWg3O_HkKy6_SyHepLK-_BR7HfcXYB6pVJCG3BW-lVMY7gl5sULFA74kNZH50h8hdmyWC9JgOHn0n3YLdaxSWlhctuwNPSwqwzY4qtN7_CZub81SXWpKiwj4UpyB10b8rM8qn35FS1hfsaFCVi0gQpd4vFDgFyqqpmiwq8oMr8RZ2mf0NMKCP3RXnMhy9Yq8O7lgG2t6g1g9noWbzZDUZNc54tv4WGFJ_rJZRz0jE_GR6v5sdqsDTdjFquPlQ", + "e": "AQAB" + } + ] + }, + "token_endpoint_auth_method": "private_key_jwt" +} +``` + +And with `jwks_uri`: + +``` +{ + "client_id": "client-jwks-uri", + "jwks_uri": "http://path-to-my-public/keys.json", + "token_endpoint_auth_method": "private_key_jwt" +} +``` + +The `jwks_uri` must return a JSON object containing the public keys associated with the OAuth 2.0 Client: + +``` +{ + "keys": [ + { + "kty": "RSA", + "n": "jL7h5wc-yeMUsHGJHc0xe9SbTdaLKXMHvcIHQck20Ji7SvrHPdTDQTvZtTDS_wJYbeShcCrliHvbJRSZhtEe0mPJpyWg3O_HkKy6_SyHepLK-_BR7HfcXYB6pVJCG3BW-lVMY7gl5sULFA74kNZH50h8hdmyWC9JgOHn0n3YLdaxSWlhctuwNPSwqwzY4qtN7_CZub81SXWpKiwj4UpyB10b8rM8qn35FS1hfsaFCVi0gQpd4vFDgFyqqpmiwq8oMr8RZ2mf0NMKCP3RXnMhy9Yq8O7lgG2t6g1g9noWbzZDUZNc54tv4WGFJ_rJZRz0jE_GR6v5sdqsDTdjFquPlQ", + "e": "AQAB" + } + ] +} +``` + +## OpenID Connect + +### Subject Identifier Algorithms + +Hydra supports two [Subject Identifier Algorithms](http://openid.net/specs/openid-connect-core-1_0.html#SubjectIDTypes): + +* `public`: This provides the same `sub` (subject) value to all Clients (default). +* `pairwise`: This provides a different `sub` value to each Client, so as not to enable Clients to +correlate the End-User's activities without permission. + +You can enable either one or both algorithms using the `OIDC_SUBJECT_TYPES_SUPPORTED` environment variable: + +* `export IDC_SUBJECT_TYPES_SUPPORTED=public` (default) +* `export IDC_SUBJECT_TYPES_SUPPORTED=pairwise` +* `export IDC_SUBJECT_TYPES_SUPPORTED=public,pairwise` + +If `pairwise` is enabled, you must also set the environment variable `OIDC_SUBJECT_TYPE_PAIRWISE_SALT`. The salt +is used to obfuscate the `sub` value. + +**This value should not be changed once set in production. Changing it will cause all client applications +to receive new user IDs from ORY Hydra which will lead to serious complications with authentication on their side!** + +Each OAuth 2.0 Client has a configuration field `subject_type`. The value of that `subject_type` is either `public` or +`pairwise`. If the mode is enabled by `IDC_SUBJECT_TYPES_SUPPORTED`, then ORY Hydra will choose the right strategy automatically. + +While ORY Hydra handles `sub` obfuscation out of the box, you may also override this value with your own obfuscated +`sub` value by setting `force_subject_identifier` when accepting the login challenge in your user login app. + +## System Secret Rotation + +We advise to rotate the system secret from time to time. The system secret is used to sign and validate OAuth 2.0 Access +and Refresh Tokens and to encrypt JSON Web Keys in the store. If you use the JWT strategy for OAuth 2.0 Access Tokens, +the system secret has no effect on these. + +To rotate the system secret (only possible with SQL at the moment), follow this guide: + +1. Shutdown all ORY Hydra instances +2. Run `OLD_SYSTEM_SECRET=foo NEW_SYSTEM_SECRET=bar hydra migrate secret db://url/...` +3. Decide if access/refresh tokens signed with the old key should still be valid. + * If yes, set `ROTATED_SYSTEM_SECRET` to the old secret before starting `hydra serve ...`, and `SYSTEM_SECRET` to the new one. + * If not, set only `SYSTEM_SECRET` to the new secret before running `hydra serve ...`. +4. Restart ORY Hydra instances. diff --git a/docs/hydra/appendix.md b/docs/hydra/appendix.md new file mode 100644 index 000000000..63d7f614b --- /dev/null +++ b/docs/hydra/appendix.md @@ -0,0 +1,287 @@ +--- +id: hydra-appendix +title: Appendix +--- + +## `serve` configuration and flags + +``` +$ hydra help serve + +ORY Hydra exposes two ports, a public and an administrative port. The public port is responsible +for handling requests from the public internet, such as the OAuth 2.0 Authorize and Token URLs. The administrative +port handles administrative requests like creating OAuth 2.0 Clients, managing JSON Web Keys, and managing User Login +and Consent sessions. + +It is recommended to run "hydra serve all". If you need granular control over CORS settings or similar, you may +want to run "hydra serve admin" and "admin serve public" separately. + +To learn more about each individual command, run: + +- hydra help serve all +- hydra help serve admin +- hydra help serve public + +All sub-commands share command line flags and the following environment variable names: + +CORE CONTROLS +============= + +- DATABASE_URL: A URL to a persistent backend. Hydra supports various backends: + - Memory: If DATABASE_URL is "memory", data will be written to memory and is lost when you restart this instance. + Example: DATABASE_URL=memory + + - Postgres: If DATABASE_URL is a DSN starting with postgres:// PostgreSQL will be used as storage backend. + Example: DATABASE_URL=postgres://user:password@host:123/database + + If PostgreSQL is not serving TLS, append ?sslmode=disable to the url: + DATABASE_URL=postgres://user:password@host:123/database?sslmode=disable + + - MySQL: If DATABASE_URL is a DSN starting with mysql:// MySQL will be used as storage backend. + Example: DATABASE_URL=mysql://user:password@tcp(host:123)/database?parseTime=true + + Be aware that the ?parseTime=true parameter is mandatory, or timestamps will not work. + +- SYSTEM_SECRET: A secret that is at least 16 characters long. If none is provided, one will be generated. They key + is used to encrypt sensitive data using AES-GCM (256 bit) and validate HMAC signatures. + Example: SYSTEM_SECRET=jf89-jgklAS9gk3rkAF90dfsk + +- COOKIE_SECRET: A secret that is used to encrypt cookie sessions. Defaults to SYSTEM_SECRET. It is recommended to use + a separate secret in production. + Example: COOKIE_SECRET=fjah8uFhgjSiuf-AS + +- PORT: The port hydra should listen on. + Defaults to PORT=4444 + +- HOST: The host interface hydra should listen on. Leave empty to listen on all interfaces. + Example: HOST=localhost + +- BCRYPT_COST: Set the bcrypt hashing cost. This is a trade off between + security and performance. Range is 4 =< x =< 31. + Defaults to BCRYPT_COST=10 + +- LOG_LEVEL: Set the log level, supports "panic", "fatal", "error", "warn", "info" and "debug". Defaults to "info". + Example: LOG_LEVEL=panic + +- LOG_FORMAT: Leave empty for text based log format, or set to "json" for JSON formatting. + Example: LOG_FORMAT="json" + +- DISABLE_TELEMETRY: Set to "1" to disable telemetry collection and sharing - for more information please + visit https://ory.gitbooks.io/hydra/content/telemetry.html + Example: DISABLE_TELEMETRY="1" + +- RESOURCE_NAME_PREFIX: Allows the alternation of the "rn:hydra:" prefix in all resource names declared by ORY Hydra. + Defaults to "rn:hydra" if empty and removes the last trailing colon. + Example: RESOURCE_NAME_PREFIX="resources:my-domain.com" + + +OAUTH2 CONTROLS +=============== + +- OAUTH2_ERROR_URL: A dedicated endpoint that shows critical errors in a user-friendly way. + Example: OAUTH2_ERROR_URL=https://id.myapp.com/error + +- OAUTH2_CONSENT_URL: The consent provider's URL. + Example: OAUTH2_CONSENT_URL=https://id.myapp.com/consent + +- OAUTH2_LOGIN_URL: The login provider's URL. + Example: OAUTH2_LOGIN_URL=https://id.myapp.com/login + +- OAUTH2_LOGOUT_REDIRECT_URL: The URL where the user's browser will be redirected to after successfully logging out + of ORY Hydra. + Example: OAUTH2_LOGOUT_REDIRECT_URL=https://myapp.com/ + +- OAUTH2_ISSUER_URL: This is the public URL of your ORY Hydra installation. It is used for OAuth2 and OpenID Connect and must be + specified and using HTTPS protocol, unless --dangerous-force-http is set. + Example: OAUTH2_ISSUER_URL=https://hydra.myapp.com/ + +- AUTH_CODE_LIFESPAN: Lifespan of OAuth2 authorize codes. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + Defaults to AUTH_CODE_LIFESPAN=10m + +- ID_TOKEN_LIFESPAN: Lifespan of OpenID Connect ID Tokens. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + Defaults to ID_TOKEN_LIFESPAN=1h + +- ACCESS_TOKEN_LIFESPAN: Lifespan of OAuth2 access tokens. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + Defaults to ACCESS_TOKEN_LIFESPAN=1h + +- LOGIN_CONSENT_REQUEST_LIFESPAN: Maximum lifespan of a login and consent request. This specifies the maximum time a user + may take to complete the login and consent flow, before that requests times out and results in an error. Valid time + units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + Defaults to LOGIN_CONSENT_REQUEST_LIFESPAN=15m + +- SCOPE_STRATEGY: Set this to DEPRECATED_HIERARCHICAL_SCOPE_STRATEGY to enable the deprecated hierarchical scope strategy. + This is required if you do not want to migrate to the new wildcard strategy. + +- OAUTH2_SHARE_ERROR_DEBUG: Set this to true if you want to share error debugging information with your OAuth 2.0 clients. + Keep in mind that debug information is very valuable when dealing with errors, but might also expose database error + codes and similar errors. + Defaults to OAUTH2_SHARE_ERROR_DEBUG=false + +- OAUTH2_ACCESS_TOKEN_STRATEGY: Sets the Access Token Strategy. Defaults to "opaque" which is the recommended strategy + for usage with ORY Hydra. If set to "jwt", then Access Tokens will be a signed JSON Web Token. The public key + for verifying the token can be obtained from "./well-known/jwks.json". Please note that the "jwt" strategy is currently + in BETA and not recommended for production just yet. + Defaults to OAUTH2_ACCESS_TOKEN_STRATEGY="opaque" + + +OPENID CONNECT CONTROLS +=============== + +- OIDC_DISCOVERY_CLAIMS_SUPPORTED: A comma separated list of supported claims to be advertised at the OpenID Connect + Discovery endpoint /.well-known/openid-configuration. Always adds "sub" to the supported claims. + +- OIDC_DISCOVERY_SCOPES_SUPPORTED: A comma separated list of supported scopes to be advertised at the OpenID Connect + Discovery endpoint /.well-known/openid-configuration. Always adds "offline", "openid" to the supported scopes. + +- OIDC_DISCOVERY_USERINFO_ENDPOINT: A URL of the userinfo endpoint to be advertised at the OpenID Connect + Discovery endpoint /.well-known/openid-configuration. Defaults to ORY Hydra's userinfo endpoint at /userinfo. + Set this value if you want to handle this endpoint yourself. + +- OIDC_DYNAMIC_CLIENT_REGISTRATION_DEFAULT_SCOPE: The OpenID Connect Dynamic Client Registration specification + has no concept of whitelisting OAuth 2.0 Scope. If you want to expose Dynamic Client Registration, you should set the default + scope enabled for newly registered clients. Keep in mind that users can overwrite this default by setting the + "scope" key in the registration payload, effectively disabling the concept of whitelisted scopes. + Example: OIDC_DYNAMIC_CLIENT_REGISTRATION_DEFAULT_SCOPE=openid,offline,scope-a,scope-b + +- OIDC_SUBJECT_TYPES_SUPPORTED: Sets which identifier algorithms (comma-separated) should be supported. + Can be "public" or "pairwise" or both. Defaults to "public". Please note that "pairwise" does not work with the + JWT OAuth 2.0 Access Token Strategy. + Example: OIDC_SUBJECT_TYPES_SUPPORTED=public,pairwise + +- OIDC_SUBJECT_TYPE_PAIRWISE_SALT: Is the salt of the pairwise identifier algorithm and must be set if pairwise is enabled. + The length must be longer than 8 characters. + + !! Warning !! + This value should not be changed once set in production. Changing it will cause all client applications + to receive new user IDs from ORY Hydra which will lead to serious complications with authentication on their side! + + Example: OIDC_SUBJECT_TYPE_PAIRWISE_SALT=5be780ef690045aebf50845d56acd72c + +HTTPS CONTROLS +============== + +- HTTPS_ALLOW_TERMINATION_FROM: Whitelist one or multiple CIDR address ranges and allow them to terminate TLS connections. + Be aware that the X-Forwarded-Proto header must be set and must never be modifiable by anyone but + your proxy / gateway / load balancer. Supports ipv4 and ipv6. + Hydra serves http instead of https when this option is set. + Example: HTTPS_ALLOW_TERMINATION_FROM=127.0.0.1/32,192.168.178.0/24,2620:0:2d0:200::7/32 + +- HTTPS_TLS_CERT_PATH: The path to the TLS certificate (pem encoded). + Example: HTTPS_TLS_CERT_PATH=~/cert.pem + +- HTTPS_TLS_KEY_PATH: The path to the TLS private key (pem encoded). + Example: HTTPS_TLS_KEY_PATH=~/key.pem + +- HTTPS_TLS_CERT: Base64 encoded (without padding) string of the TLS certificate (PEM encoded) to be used for HTTP over TLS (HTTPS). + Example: HTTPS_TLS_CERT="-----BEGIN CERTIFICATE-----\nMIIDZTCCAk2gAwIBAgIEV5xOtDANBgkqhkiG9w0BAQ0FADA0MTIwMAYDVQQDDClP..." + +- HTTPS_TLS_KEY: Base64 encoded (without padding) string of the private key (PEM encoded) to be used for HTTP over TLS (HTTPS). + Example: HTTPS_TLS_KEY="-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDg..." + + +CORS CONTROLS +============== + +- CORS_ENABLED: Switch CORS support on (true) or off (false). Default is off (false). + + Example: CORS_ENABLED=true + +- CORS_ALLOWED_ORIGINS: A list of origins (comma separated values) a cross-domain request can be executed from. + If the special * value is present in the list, all origins will be allowed. An origin may contain a wildcard (*) + to replace 0 or more characters (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penality. + Only one wildcard can be used per origin. The default value is *. + + Example: CORS_ALLOWED_ORIGINS=http://*.domain.com,http://*.domain2.com + +- CORS_ALLOWED_METHODS: A list of methods (comma separated values) the client is allowed to use with cross-domain + requests. Default value is simple methods (GET and POST). + + Example: CORS_ALLOWED_METHODS=POST,GET,PUT + +- CORS_ALLOWED_CREDENTIALS: Indicates whether the request can include user credentials like cookies, HTTP authentication + or client side SSL certificates. + + Default: CORS_ALLOWED_CREDENTIALS=false + Example: CORS_ALLOWED_CREDENTIALS=true + +- CORS_DEBUG: Debugging flag adds additional output to debug server side CORS issues. + + Default: CORS_DEBUG=false + Example: CORS_DEBUG=true + +- CORS_MAX_AGE: Indicates how long (in seconds) the results of a preflight request can be cached. The default is 0 + which stands for no max age. + + Default: CORS_MAX_AGE=0 + Example: CORS_MAX_AGE=10 + +- CORS_ALLOWED_HEADERS: A list of non simple headers (comma separated values) the client is allowed to use with + cross-domain requests. + +- CORS_EXPOSED_HEADERS: Indicates which headers (comma separated values) are safe to expose to the API of a + CORS API specification. + +DEBUG CONTROLS +============== + +- TRACING_PROVIDER: Set this to the tracing backend you wish to use. + + Supported tracing backends: [jaeger] + + Example: TRACING_PROVIDER=jaeger + +- TRACING_PROVIDER_JAEGER_SAMPLING_SERVER_URL: The address of jaeger-agent's HTTP sampling server + + Example: TRACING_PROVIDER_JAEGER_SAMPLING_SERVER_URL=http://localhost:5778/sampling + +- TRACING_PROVIDER_JAEGER_SAMPLING_TYPE: The type of the sampler you want to use + + Supported values: [const, probabilistic, ratelimiting] + + Default: const + + Example: TRACING_PROVIDER_JAEGER_SAMPLING_TYPE=const + +- TRACING_PROVIDER_JAEGER_SAMPLING_VALUE: The value passed to the sampler type that has been configured. + + Supported values: This is dependant on the sampling strategy used: + - const: 0 or 1 (all or nothing) + - rateLimiting: a constant rate (e.g. setting this to 3 will sample requests with the rate of 3 traces per second) + - probabilistic: a value between 0..1 + + Example: TRACING_PROVIDER_JAEGER_SAMPLING_VALUE=1 + +- TRACING_PROVIDER_JAEGER_LOCAL_AGENT_ADDRESS: The address of the jaeger-agent where spans should be sent to + + Example: TRACING_PROVIDER_JAEGER_LOCAL_AGENT_ADDRESS=127.0.0.1:6831 + +- TRACING_SERVICE_NAME: Specifies the service name to use on the tracer. + + Default: ORY Hydra + + Example: TRACING_SERVICE_NAME="ORY Hydra" + + +- PROFILING: Set "PROFILING=cpu" to enable cpu profiling and "PROFILING=memory" to enable memory profiling. + It is not possible to do both at the same time. DProfiling is disabled per default. + + Example: PROFILING=cpu + +Usage: + hydra serve [command] + +Available Commands: + admin Serves Administrative HTTP/2 APIs + all Serves both public and administrative HTTP/2 APIs + public Serves Public HTTP/2 APIs + +Flags: + --dangerous-force-http Disable HTTP/2 over TLS (HTTPS) and serve HTTP instead. Never use this in production. + --disable-telemetry Disable anonymized telemetry reports - for more information please visit https://www.ory.sh/docs/guides/telemetry + -h, --help help for serve + +Global Flags: + --skip-tls-verify Foolishly accept TLS certificates signed by unkown certificate authorities + +Use "hydra serve [command] --help" for more information about a command. +``` \ No newline at end of file diff --git a/docs/hydra/configure-deploy.md b/docs/hydra/configure-deploy.md new file mode 100644 index 000000000..4f353b0c3 --- /dev/null +++ b/docs/hydra/configure-deploy.md @@ -0,0 +1,322 @@ +--- +id: hydra-configure-deploy +title: Configure and Deploy +--- + +The goal of this chapter is to introduce you to a fully functional set up that includes ORY Hydra as well as our +User Login & Consent Provider reference implementation. + +The goal of this section is to familiarize you with the specifics of setting up ORY Hydra in your environment. +Before starting with this section, please check out the [tutorial](5min-tutorial.md). It will teach you the most important flows +and settings for Hydra. + +This guide will: + +1. Download and run a PostgreSQL container in Docker. +2. Download and run ORY Hydra in Docker. +3. Download and run our reference User Login & Consent Provider. +4. Create an OAuth 2.0 Client to perform the OAuth 2.0 Authorize Code Flow. +5. Perform the OAuth 2.0 Authorize Code flow. + +Before starting with this guide, please install the most recent version of [Docker](https://www.docker.com/community-edition#/download). +While docker is not required for running ORY Hydra, we recommend using it for this tutorial as it will greatly reduce +the complexity of setting up a database on your system without virtualization, installing Go, and compiling ORY Hydra. + +## Create a Network + +Before we can start, a network must be created which we will attach all our Docker containers to. That way, the containers +can talk to one another. + +``` +$ docker network create hydraguide +``` + +## Deploy PostgreSQL + +For the purpose of this tutorial, we will use PostgreSQL as a database. As you probably already know, don't run databases in Docker in production! +For the sake of this tutorial however, let's use Docker to quickly deploy the database. + +``` +$ docker run \ + --network hydraguide \ + --name ory-hydra-example--postgres \ + -e POSTGRES_USER=hydra \ + -e POSTGRES_PASSWORD=secret \ + -e POSTGRES_DB=hydra \ + -d postgres:9.6 +``` + +This command wil start a postgres instance with name `ory-hydra-example--postgres`, set up a database called `hydra` +and create a user `hydra` with password `secret`. + +## Deploy ORY Hydra + +We highly recommend using Docker to run Hydra, as installing, configuring and running Hydra is easiest with Docker. +ORY Hydra is available on [Docker Hub](https://hub.docker.com/r/oryd/hydra/). + +``` +# The system secret can only be set against a fresh database. Key rotation is currently not supported. This +# secret is used to encrypt the database and needs to be set to the same value every time the process (re-)starts. +# You can use /dev/urandom to generate a secret. But make sure that the secret must be the same anytime you define it. +# You could, for example, store the value somewhere. +$ export SYSTEM_SECRET=$(export LC_CTYPE=C; cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) +# +# Alternatively you can obviously just set a secret: +# $ export SYSTEM_SECRET=this_needs_to_be_the_same_always_and_also_very_$3cuR3-._ + +# The database url points us at the postgres instance. This could also be an ephermal in-memory database (`export DATABASE_URL=memory`) +# or a MySQL URI. +$ export DATABASE_URL=postgres://hydra:secret@ory-hydra-example--postgres:5432/hydra?sslmode=disable + +# Before starting, let's pull the latest ORY Hydra tag from docker. +$ docker pull oryd/hydra:v1.0.0-beta.8 + +# This command will show you all the environment variables that you can set. Read this carefully. +# It is the equivalent to `hydra help serve`. +$ docker run -it --rm --entrypoint hydra oryd/hydra:v1.0.0-beta.8 help serve + +Starts all HTTP/2 APIs and connects to a database backend. +[...] + +# ORY Hydra does not do magic, it requires conscious decisions, for example running SQL migrations which is required +# when installing a new version of ORY Hydra, or upgrading an existing installation. +# It is the equivalent to `hydra migrate sql postgres://hydra:secret@ory-hydra-example--postgres:5432/hydra?sslmode=disable` +$ docker run -it --rm \ + --network hydraguide \ + oryd/hydra:v1.0.0-beta.8 \ + migrate sql $DATABASE_URL + +Applying `client` SQL migrations... +[...] +Migration successful! + +# Let's run the server (settings explained below): +$ docker run -d \ + --name ory-hydra-example--hydra \ + --network hydraguide \ + -p 9000:4444 \ + -p 9001:4445 \ + -e SYSTEM_SECRET=$SYSTEM_SECRET \ + -e DATABASE_URL=$DATABASE_URL \ + -e OAUTH2_ISSUER_URL=https://localhost:9000/ \ + -e OAUTH2_CONSENT_URL=http://localhost:9020/consent \ + -e OAUTH2_LOGIN_URL=http://localhost:9020/login \ + oryd/hydra:v1.0.0-beta.8 serve all + +# And check if it's running: +$ docker logs ory-hydra-example--hydra + +time="2017-06-29T21:26:26Z" level=info msg="Connecting with postgres://*:*@postgres:5432/hydra?sslmode=disable" +time="2017-06-29T21:26:26Z" level=info msg="Connected to SQL!" +[...] +time="2017-06-29T21:26:34Z" level=info msg="Setting up http server on :4444" +``` + +Let's dive into the various settings: + +* `--network hydraguide` connects this instance to the network and makes it possible to connect to the PostgreSQL database. +* `-p 9000:4444` exposes ORY Hydra's public API on `https://localhost:9000/`. +* `-p 9001:4445` exposes ORY Hydra's administrative API on `https://localhost:9001/`. +* `-e SYSTEM_SECRET=$SYSTEM_SECRET` sets the system secret environment variable **(required)**. +* `-e DATABASE_URL=$DATABASE_URL` sets the database url environment variable **(required)**. +* `-e OAUTH2_ISSUER_URL=https://localhost:9000/` this value must be set to the publicly available URL of ORY Hydra **(required)**. +* `-e OAUTH2_CONSENT_URL=http://localhost:9020/consent` this sets the URL of the consent provider **(required)**. We will set up the service +that handles requests at that URL in the next sections. +* `-e OAUTH2_LOGIN_URL=http://localhost:9020/login` this sets the URL of the login provider **(required)**. We will set up the service +that handles requests at that URL in the next sections. + +Note: In this example we did not define a value for the optional setting `OAUTH2_ERROR_URL`. This URL can be used +to provide an endpoint which will receive error messages from ORY Hydra that should be displayed +to the end user. The URL receives `error` and `error_description` parameters. If this value is not set, +Hydra uses the fallback endpoint `/oauth2/fallbacks/error` and displays a default error message. In order to obtain +a uniform UI, you might want to include such an endpoint in your login or consent provider. + +To confirm that the instance is running properly, [open the health check](https://localhost:9001/health/status). If asked, +accept the self signed certificate in your browser. You should simply see `ok`. + +On start up, ORY Hydra is initializing some values. Let's take a look at the logs: + +``` +$ docker logs ory-hydra-example--hydra +time="2017-06-30T09:06:34Z" level=info msg="Connecting with postgres://*:*@postgres:5432/hydra?sslmode=disable" +time="2017-06-30T09:06:34Z" level=info msg="Connected to SQL!" +time="2017-06-30T09:06:34Z" level=info msg="Key pair for signing hydra.openid.id-token is missing. Creating new one." +time="2017-06-30T09:06:41Z" level=warning msg="No TLS Key / Certificate for HTTPS found. Generating self-signed certificate." +time="2017-06-30T09:06:41Z" level=info msg="Setting up http server on :4444" +``` + +As you can see, the following steps are performed when running ORY Hydra against a fresh database: + +1. If no system secret was given (in our case we provided one), a random one is generated and emitted to the logs. +Note this down, otherwise you won't be able to restart Hydra. +2. Cryptographic keys are generated for the OpenID Connect ID Token, the consent challenge and response, and TLS encryption +using a self-signed certificate, which is why we need to run all commands using `--skip-tls-verify`. + +ORY Hydra can be managed using the Hydra Command Line Interface (CLI), which is using ORY Hydra's REST APIs. To +see the available commands, run: + +``` +$ docker run --rm -it --entrypoint hydra oryd/hydra:v1.0.0-beta.8 help +Hydra is a cloud native high throughput OAuth2 and OpenID Connect provider + +Usage: + hydra [command] + +[...] +``` + +### Without Docker + +You can also install ORY Hydra without docker. For the purpose of this tutorial, [please skip this section for now](#configure-ory-hydra), and read +it later. + +#### Download Binaries + +The client and server **binaries are downloadable at the [releases tab](https://github.com/ory/hydra/releases)**. +There is currently no installer available. You have to add the Hydra binary to the PATH environment variable yourself or put +the binary in a location that is already in your `$PATH` (e.g. `/usr/bin`, ...). + +Once installed, you should be able to run: + +``` +$ hydra help + +Hydra is a cloud native high throughput OAuth2 and OpenID Connect provider + +Usage: + hydra [command] + +Available Commands: + clients Manage OAuth2 clients +... +``` + +#### Build from Source + +If you wish to compile ORY Hydra yourself, you need to install and set up [Go 1.11+](https://golang.org/) and add `$GOPATH/bin` +to your `$PATH`. + +The following commands will check out the latest release tag of ORY Hydra and compile it and set up flags so that `hydra version` +works as expected. Please note that this will only work with a linux shell like bash or sh. + +``` +go get -d -u github.com/ory/hydra +cd $(go env GOPATH)/src/github.com/ory/hydra +HYDRA_LATEST=$(git describe --abbrev=0 --tags) +git checkout $HYDRA_LATEST +GO111MODULE=on go install \ + -ldflags "-X github.com/ory/hydra/cmd.Version=$HYDRA_LATEST -X github.com/ory/hydra/cmd.BuildTime=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/hydra/cmd.GitHash=`git rev-parse HEAD`" \ + github.com/ory/hydra +git checkout master +hydra help + +... +``` + +## Deploy Login & Consent App + +The Login Provider and Consent Provider can be two separate web services. We provide a [reference implementation](https://github.com/ory/hydra-login-consent-node) which +combines both features in one app. Here, we will use deploy that app using Docker. + +``` +$ docker pull oryd/hydra-login-consent-node:v1.0.0-beta.8 +$ docker run -d \ + --name ory-hydra-example--consent \ + -p 9020:3000 \ + --network hydraguide \ + -e HYDRA_URL=https://ory-hydra-example--hydra:4445 \ + -e NODE_TLS_REJECT_UNAUTHORIZED=0 \ + oryd/hydra-login-consent-node:v1.0.0-beta.8 + +# Let's check if it's running ok: +$ docker logs ory-hydra-example--consent +``` + +Let's take a look at the arguments: +* `-p 9020:3000` exposes this service at port 9020. If you remember, that's the port of the `OAUTH2_CONSENT_URL` and `OAUTH2_LOGIN_URL` value +from the ORY Hydra docker container (`OAUTH2_CONSENT_URL=http://localhost:9020/consent`, `OAUTH2_LOGIN_URL=http://localhost:9020/login`). +* `HYDRA_URL=http://hydra:4445` point to the ORY Hydra Administrative API. +* `NODE_TLS_REJECT_UNAUTHORIZED=0` disables TLS verification, because we are using self-signed certificates. + +## Perform OAuth 2.0 Flow + +Great! Our infrastructure is all set up! Next it's time to perform the OAuth 2.0 Authorize Code flow. For that purpose, +the ORY Hydra CLI has a feature that sets up an OAuth 2.0 Consumer and an OAuth 2.0 callback URL. Typically, this would +be a third-party application that requests access to a user's resources on your servers - for example a Facebook App you wrote +that backs up a user's photos and thus requires read access to the user's photos. + +Before we go ahead, the OAuth 2.0 Client that performs the request has to be set up. Let's call the client `facebook-photo-backup`. +We have to specify which OAuth 2.0 Grant Types, OAuth 2.0 Scope, OAuth 2.0 Response Types, and Callback URLs the client may request: + +``` +$ docker run --rm -it \ + -e HYDRA_ADMIN_URL=https://ory-hydra-example--hydra:4445 \ + --network hydraguide \ + oryd/hydra:v1.0.0-beta.8 \ + clients create --skip-tls-verify \ + --id facebook-photo-backup \ + --secret some-secret \ + --grant-types authorization_code,refresh_token,client_credentials,implicit \ + --response-types token,code,id_token \ + --scope openid,offline,photos.read \ + --callbacks http://127.0.0.1:9010/callback + +Client id: hydra-facebook-photo-backup +Client Secret: some-secret +``` + +Let's dive into some of the arguments: +* `--skip-tls-verify` is supported by all management commands (create/delete/update/... OAuth 2.0 Client, JSON Web Key, ...) + and tells the CLI to trust any certificate authority - even self-signed ones. We need this flag because the server + uses a self-signed certificate. In production deployments, you would use a certificate signed by a trusted CA. +* `--grant-types authorize_code,refresh_token,client_credentials,implicit` we want to be able to perform all of these +OAuth 2.0 flows. +* `--response-types token,code,id_token` allows us to receive authorize codes, access and refresh tokens, and +OpenID Connect ID Tokens. +* `--scope openid,offline,fotos.read` allows the client to request various permissions: + * `openid` allows the client to perform the OpenID Connect flow and request an OpenID Connect ID Token. + * `offline` allows the client to request a refresh token. Because we want to continuously backup photos, the app must be + able to refresh expired access tokens. This scope allows that. + * `photos.read` this is an imaginary scope that is not handled by ORY Hydra but serves the purpose of making it clear that + we could request read access to a user's photos. You can obviously omit this scope or use your own scope. +* `--callbacks http://localhost:9010/callback` allows the client to request this redirect uri. + +Perfect, let's perform an exemplary OAuth 2.0 Authorize Code Flow! To make this easy, the ORY Hydra CLI provides +a helper command called `hydra token user`. Just imagine this being, for example, passport.js that is generating +an auth code url, redirecting the browser to it, and then exchanging the authorize code for an access token. The +same thing happens with this command: + +``` +$ docker run --rm -it \ + --network hydraguide \ + -p 9010:9010 \ + oryd/hydra:v1.0.0-beta.8 \ + token user --skip-tls-verify \ + --port 9010 \ + --auth-url https://localhost:9000/oauth2/auth \ + --token-url https://ory-hydra-example--hydra:4444/oauth2/token \ + --client-id facebook-photo-backup \ + --client-secret some-secret \ + --scope openid,offline,photos.read + +Setting up callback listener on http://localhost:9010/callback +Press ctrl + c on Linux / Windows or cmd + c on OSX to end the process. +If your browser does not open automatically, navigate to: + + https://localhost:9010/ +``` + +open the link, as prompted, in your browser, and follow the steps shown there. You might encounter a screen like the following +one: + +![Insecure connection](../../images/docs/hydra/insecure-connection.png) + +This happens because we run ORY Hydra with a self-signed TLS certificate. In production deployments, you would probably +use a certificate signed by a trusted CA and not see this screen. + +When you see this screen, click on "Advanced" and "Add Exception" to continue. In some browsers, this might work differently, +but it's always possible to proceed. + +When completed, you should land at a screen that looks like this one: + +![OAuth 2.0 result](../../images/docs/hydra/install-result.png) diff --git a/docs/hydra/debugging.md b/docs/hydra/debugging.md new file mode 100644 index 000000000..84bdde012 --- /dev/null +++ b/docs/hydra/debugging.md @@ -0,0 +1,202 @@ +--- +id: hydra-debugging +title: Debugging +--- + +Spec-compliant OAuth 2.0 and OpenID Connect is hard. Let's take a look how to resolve certain issues. + + + +## First Aid + +There are three things you can do to quickly debug any issue: + +1. Check the logs. ORY Hydra has extensive logging and you will find the issue most likely in the logs. Here is an example +log line for a client that requested a redirect URL that did not match the whitelisted redirect URLS: `time="2018-08-07T16:01:16Z" level=error msg="An error occurred" description="The request is missing a required parameter, includes an invalid parameter value, includes a parameter more than once, or is otherwise malformed" error=invalid_request hint="The \"redirect_uri\" parameter does not match any of the OAuth 2.0 Client's pre-registered redirect urls."` +2. Check the URL because of two reasons: + 1. ORY Hydra sends `error_hint`, `error`, `error_description`, `error_debug` in the URL. You will find the + cause of the error most likely in there. + 2. You are maybe in the wrong URL. Make sure the host and port and path is correct. This happens often, especially + when you're just starting out and experimenting +3. Set environment variable `OAUTH2_SHARE_ERROR_DEBUG=true`. Do not do this in production, it is possible, though unlikely, +that important data leaks with this. If set to true, ORY Hydra will set the `error_debug` query parameter if debug +information is available. +4. If you're just starting out and experimenting your docker set up does not work at all: + 1. Stop all containers + 2. Remove them (unless you have something important running) + 3. Retry. **This can help a lot if you are new to this!** + +## OAuth 2.0 Authorize Code Flow fails + +The most likely cause is misconfiguration, summarized in the next sections. + +### Wrong or misconfigured OAuth 2.0 Client + +You are using the wrong OAuth 2.0 Client or the OAuth 2.0 Client has a broken configuration. To check that +you're using the right client, run: + +``` +hydra clients get --endpoint http://ory-hydra +``` + +The result shows you the whole client (excluding its secret). Check that the values are correct. Example: + +``` +{ + "client_id": "my-client", + "grant_types": [ + "authorization_code" + ], + "jwks": {}, + "redirect_uris": [ + "http://127.0.0.1:5556/callback" + ], + "response_types": [ + "code" + ], + "scope": "openid offline", + "subject_type": "pairwise", + "token_endpoint_auth_method": "client_secret_basic", + "userinfo_signed_response_alg": "none" +} +``` + +### Redirect URL is not whitelisted + +A likely cause of your request failing is that you are using the wrong redirect URL. Assuming your OAuth 2.0 +URL looks like `http://ory-hydra/oauth2/auth?client_id=my-client&...&redirect_uri=http://my-url/callback` + +The redirect URL `http://my-url/callback` must be whitelisted in your client configuration. The URLs must match **exactly**. +URL `http://my-url/callback` and `http://my-url/callback?foo=bar` are different URLs! + +To see the whitelisted redirect_uris, check the client: + +``` +hydra clients get --endpoint http://ory-hydra + +{ + // ... + "redirect_uris": [ + "http://127.0.0.1:5556/callback" + ], + // ... +} +``` + +Here you see that `http://my-url/callback` is not in the list, which is why the request fails. + +### OAuth 2.0 Client ID and secret are sent in body instead of header + +There are multiple ways of authenticating OAuth 2.0 Clients at the `/oauth2/token`: + +* HTTP Basic Authorization (`client_secret_basic`) - the OAuth 2.0 Client ID and secret are sent in the HTTP Header (`Authorization: basic ....`) +* HTTP Body (`client_secret_post`) - the OAuth 2.0 Client ID and secret are sent in the POST body (`Content-Type: application/x-www-form-urlencoded`) + +Both are valid schemes. But the OAuth 2.0 Client has to be configured to allow either of the one. Per default, the OAuth 2.0 +Client allows HTTP Basic Authorization only. You can check which method is allowed: + +``` +hydra clients get --endpoint http://ory-hydra +{ + // ... + "token_endpoint_auth_method": "client_secret_basic", + // ... +} +``` + +As you can see, this client is allowed to authorize using HTTP Basic Authorization. If you try to authorize with the client +credentials in the POST body, the authentication process will fail. To allow a client to perform the POST authorization +scheme, you must set `"token_endpoint_auth_method": "client_secret_post"`. You can do this in the CLI with the +`--token-endpoint-auth-method` flag. + +## Distributed Tracing + +### What is this? + +Configuring Distributed Tracing (DT) will enable you to obtain a visualization of the call paths that take place in order to process a request made to Hydra. It's yet another tool that you can use to aid you in profiling, debugging and ultimately understanding your deployment of Hydra better. Hydra currently supports the following tracing options: + +- Tracing backend(s): Jaeger - _Note: adding support for other [opentracing compliant backends](https://opentracing.io/docs/supported-tracers) is planned. To aid in priority, please [create an issue](https://github.com/ory/hydra/issues) with your feature request._ +- Following existing traces: If you have deployed Hydra behind a proxy that has initiated a trace, Hydra will attempt to join that trace by examining the request headers for tracing context. + +### What a Hydra trace includes + +In DT speak, a trace is comprised of one or more spans which are logical units of work. Each Hydra span is encapsulated with the following state: + +- A name +- A start time +- A finish time +- A set of zero or more tags + +Hydra currently creates the following spans: + +- Top level span (_named after the request path_) for the requested endpoint. Span tags: + - http method + - http status code + - error IFF status code >= 400 +- Child span will be created if bcrypt (_e.g. when the token endpoint is called_) is called. Span tags: + - bcrypt work factor +- All SQL database interactions. Spans/tags will vary depending on the database driver used. + +This is still evolving and subject to change as tracing support continues to expand in Hydra. If you see something that is missing/wrong, please create an issue. + +### Alright, how can I set this up locally? + +The [provided docker-compose file](https://github.com/ory/hydra/blob/master/docker-compose.yml) in the project repository has tracing configuration w/ jaeger added which you can use to play around with. Simply uncomment the configurations associated with tracing as so: + +**Under the Hydra service definition `depends_on` configs, uncomment the following:** + +``` +- jaeger +``` + +**Under the Hydra service definition `environment` configs, uncomment the following:** + +``` +- TRACING_PROVIDER +- TRACING_PROVIDER_JAEGER_SAMPLING_SERVER_URL +- TRACING_PROVIDER_JAEGER_LOCAL_AGENT_ADDRESS +- TRACING_PROVIDER_JAEGER_SAMPLING_TYPE +- TRACING_PROVIDER_JAEGER_SAMPLING_VALUE +``` + +**Uncomment the Jaeger service definition:** + +``` +jaeger: + image: jaegertracing/all-in-one:1.7.0 + ports: + - "5775:5775/udp" + - "6831:6831/udp" + - "6832:6832/udp" + - "5778:5778" + - "16686:16686" + - "14268:14268" + - "9411:9411" +``` + +Then simply run `docker-compose up`. Grab a coffee or stretch while you wait for everything to come up. You will then be able to navigate to the Jaeger UI +which you have exposed on port `16686` at http://localhost:16686/search. You can now start making requests to Hydra and inspect traces! + +As an example, here is a trace created by making a bad request to the `POST /clients` endpoint: + +![Sample Trace](../../images/docs/hydra/sample_trace.png) + +At a glance, you are able to see that: + +- The request failed +- The request took ~80ms +- It resulted in a 409 +- The hash comparison to validate the client's credentials took a whopping 70ms. Bcrypt is expensive! +- The various database operations performed + +*Note: in order to see spans around database interactions, you must be using a SQL backend (i.e. MySQL or Postgres).* + +### Tracing configurations + +The CLI will provide you with the list of Hydra tracing configurations and their supported values. Simply run: + +``` +docker exec -it hydra_hydra_1 hydra serve --help +``` + +And read the section on `DEBUG CONTROLS`. \ No newline at end of file diff --git a/docs/hydra/dependencies-environment.md b/docs/hydra/dependencies-environment.md new file mode 100644 index 000000000..134df3649 --- /dev/null +++ b/docs/hydra/dependencies-environment.md @@ -0,0 +1,65 @@ +--- +id: hydra-dependencies-environment +title: Dependencies & Environment +--- + +ORY Hydra is built cloud native and implements [12factor](https://www.12factor.net/) principles. The Docker Image is 5 MB light +and versioned with [verbose upgrade instructions](https://github.com/ory/hydra/blob/master/UPGRADE.md) +and [detailed changelogs](https://github.com/ory/hydra/blob/master/CHANGELOG.md). Auto-scaling, migrations, health checks, +it all works with zero additional work required. It is possible to run ORY Hydra on any platform, including but not limited +to OSX, Linux, Windows, ARM, FreeBSD and more. + +ORY Hydra has two operational modes: + +* In-memory: This mode does not work with more than one instance ("cluster") and any state is lost after restarting the instance. +* SQL: This mode works with more than one instance and state is not lost after restarts. + +No further dependencies are required for a production-ready instance. + +## SQL + +The SQL adapter supports two DBMS: PostgreSQL 9.6+ and MySQL 5.7+. Please note that +older MySQL versions have issues with ORY Hydra's database schema. For more information [go here](https://github.com/ory/hydra/issues/377). + +If you do run the SQL adapter, you must first create the database schema. The `hydra serve` command does not do this +automatically, instead you must run `hydra migrate sql` to create the schemas. The `hydra migrate sql` command +also runs database migrations in case of an upgrade. Please follow the [upgrade instructions](https://github.com/ory/hydra/blob/master/UPGRADE.md) +to see when you need to run this command. Always create a backup before running `hydra migrate sql`! + +Running SQL migrations in Docker is very easy, check out the [docker-compose](https://github.com/ory/hydra/blob/master/docker-compose.yml) +example to see how we did it! + +### Configuration + +Both MySQL and PostgreSQL adapters support the following settings. You can modify these settings by appending query parameters to your DSN (`postgres://user:pw@host:port/database?setting1=foo&setting2=bar`): + +* `max_conns` sets the maximum number of open connections to the database. Defaults to the number of CPUs. Example `postgres://user:pw@host:port/database?max_conns=10`. +* `max_idle_conns` sets the maximum number of connections in the idle connection pool. Defaults to the number of CPUs. Example `postgres://user:pw@host:port/database?max_idle_conns=5`. +* `max_conn_lifetime` sets the maximum amount of time (`ms`, `s`, `m`, `h`) a connection may be reused. Defaults to 0. Example `postgres://user:pw@host:port/database?max_conn_lifetime=10s`. + +#### MySQL + +On top of the settings above, MySQL supports additional settings: + +* `sql_notes`, if set to `false`, ignores MySQL notices. If left empty or set to `true`, they will be treated as warnings. Example `mysql://user:pw@host:port/database?sql_notes=false`. +* `sql_mode` sets the server-side strict mode. Read more about possible values [here](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html). + +#### PostgreSQL + +On top of the settings above, PostgreSQL supports additional settings: + +* `sslmode` sets whether or not to use SSL (default is require, this is not the default for libpq). Valid values for sslmode are: + * `disable` - No SSL + * `require` - Always SSL (skip verification) + * `verify-ca` - Always SSL (verify that the certificate presented by the + server was signed by a trusted CA) + * `verify-full` - Always SSL (verify that the certification presented by + the server was signed by a trusted CA and the server host name + matches the one in the certificate) +* `fallback_application_name` - An application_name to fall back to if one isn't provided. +* `connect_timeout` - Maximum wait for connection, in seconds. Zero or +not specified means wait indefinitely. +* `sslcert` - Cert file location. The file must contain PEM encoded data. +* `sslkey` - Key file location. The file must contain PEM encoded data. +* `sslrootcert` - The location of the root certificate file. The file +must contain PEM encoded data. diff --git a/docs/hydra/faq.md b/docs/hydra/faq.md new file mode 100644 index 000000000..89a74ec2c --- /dev/null +++ b/docs/hydra/faq.md @@ -0,0 +1,194 @@ +--- +id: hydra-faq +title: FAQ +--- + +This file keeps track of questions and discussions from Gitter and general help with various issues. + +**Please be aware that some things noted here might be highly outdated. If you find an outdated section, please +create a PR which removes or updates it!** + + + +## How can I control SQL connection limits? + +You can configure SQL connection limits by appending parameters `max_conns`, `max_idle_conns`, or `max_conn_lifetime` +to the DSN: `postgres://foo:bar@host:port/database?max_conns=12`. + +## Why is the Resource Owner Password Credentials grant not supported? + +The following is a copy of the original [comment on GitHub](https://github.com/ory/hydra/pull/297#issuecomment-294282671): + +I took a long time for this issue, primarily because I felt very uncomfortable implementing it. The ROCP grant is something from the "dark ages" of OAuth2 and there are suitable replacements for mobile clients, such as public oauth2 clients, which are supported by Hydra: https://tools.ietf.org/html/draft-ietf-oauth-native-apps-09 + +The OAuth2 Thread Model explicitly states that the ROPC grant is commonly used in legacy/migration scenarios, and + +> This grant type has higher + risk because it maintains the UID/password anti-pattern. + Additionally, because the user does not have control over the + authorization process, clients using this grant type are not limited by scope but instead have potentially the same capabilities as the + user themselves. As there is no authorization step, the ability to + offer token revocation is bypassed. + +> Because passwords are often used for more than 1 service, this + anti-pattern may also put at risk whatever else is accessible with + the supplied credential. Additionally, any easily derived equivalent + (e.g., joe@example.com and joe@example.net) might easily allow + someone to guess that the same password can be used elsewhere. + +> Impact: The resource server can only differentiate scope based on the + access token being associated with a particular client. The client + could also acquire long-lived tokens and pass them up to an + attacker's web service for further abuse. The client, eavesdroppers, + or endpoints could eavesdrop the user id and password. + +> o Except for migration reasons, minimize use of this grant type. + +- [source](https://tools.ietf.org/html/rfc6819#section-4.4.3) + +Thus, I decided to not implement the ROPC grant in Hydra. Over time, I will add documentation how to deal with mobile scenarios and similar. + +## Should I use OAuth2 tokens for authentication? + +OAuth2 tokens are like money. It allows you to buy stuff, but the cashier does not really care if the money is +yours or if you stole it, as long as it's valid money. Depending on what you understand as authentication, this is a yes and no answer: + +* **Yes:** You can use access tokens to find out which user ("subject") is performing an action in a resource provider (blog article service, shopping basket, ...). +Coming back to the money example: *You*, the subject, receives a cappuccino from the vendor (resource provider) in exchange for money (access token). +* **No:** Never use access tokens for logging people in, for example `http://myapp.com/login?access_token=...`. +Coming back to the money example: The police officer ("authentication server") will not accept money ("access token") as a proof of identity ("it's really you"). Unless he is corrupt ("vulnerable"), of course. + +In the second example ("authentication server"), you must use OpenID Connect ID Tokens. + +## How to deal with mobile apps? + +Read [this article](https://www.ory.sh/oauth2-for-mobile-app-spa-browser). + +## How should I run migrations? + +Since ORY Hydra 0.8.0, migrations are no longer run automatically on boot. This is required in production environments, +because: + +1. Although SQL migrations are tested, migrating schemas can cause data loss and should only be done consciously with +prior back ups. +2. Running a production system with a user that has right such as ALTER TABLE is a security anti-pattern. + +Thus, to initialize the database schemas, it is required to run `hydra migrate sql driver://user:password@host:port/db` before running +`hydra host`. + +## What does the installation process look like? + +1. Run `hydra migrate sql ...` on a host close to the database (e.g. a virtual machine with access to the SQL instance). + +## What does a migration process look like? + +1. Make sure a database update is required by checking the release notes. +2. Make a back up of the database. +3. Run the migration script on a host close to the database (e.g. a virtual machine with access to the SQL instance). +Schemas are usually backwards compatible, so instances running previous versions of ORY Hydra should keep working fine. +If backwards compatibility is not given, this will be addressed in the patch notes. +4. Upgrade all ORY Hydra instances. + +## How can I do this in docker? + +Many deployments of ORY Hydra use Docker. Although several options are available, we advise to extend the ORY Hydra Docker +image + +**Dockerfile** +``` +FROM oryd/hydra:tag + +ENTRYPOINT /go/bin/hydra migrate sql $DATABASE_URL +``` + +and run it in your infrastructure once. + +Additionally, *but not recommended*, it is possible to override the entry point of the ORY Hydra Docker image using CLI flag +`--entrypoint "hydra migrate sql $DATABASE_URL; hydra host"` or with `entrypoint: hydra migrate sql $DATABASE_URL; hydra host` +set in your docker compose config. + +## Can I set the log level to warn, error, debug, ...? + +Yes, you can do so by setting the environment variable `LOG_LEVEL=`. There are various levels supported: + +* debug +* info +* warn +* error +* fatal +* panic + +## How can I import TLS certificates? + +You can import TLS certificates when running `hydra host`. This can be done by setting the following environment variables: + +**Read from file** +- `HTTPS_TLS_CERT_PATH`: The path to the TLS certificate (pem encoded). +- `HTTPS_TLS_KEY_PATH`: The path to the TLS private key (pem encoded). + +**Embedded** +- `HTTPS_TLS_CERT`: A pem encoded TLS certificate passed as string. Can be used instead of TLS_CERT_PATH. +- `HTTPS_TLS_KEY`: A pem encoded TLS key passed as string. Can be used instead of TLS_KEY_PATH. + +Or by specifying the following flags: + +``` +--https-tls-cert-path string Path to the certificate file for HTTP/2 over TLS (https). You can set HTTPS_TLS_KEY_PATH or HTTPS_TLS_KEY instead. +--https-tls-key-path string Path to the key file for HTTP/2 over TLS (https). You can set HTTPS_TLS_KEY_PATH or HTTPS_TLS_KEY instead. +``` + +## Is there an HTTP API Documentation? + +[Yes](https://www.ory.sh/docs/api/hydra). + +## How can I disable HTTPS for testing? + +You can do so by running `hydra host --dangerous-force-http`. + +## MySQL gives `unsupported Scan, storing driver.Value type []uint8 into type *time.Time` + +> did a quick test to get mysql running, but run into migrate sql issue - seems mysql related +An error occurred while running the migrations: Could not apply ladon SQL migrations: Could not migrate sql schema, applied 0 migrations: sql: Scan error on column index 0: unsupported Scan, storing driver.Value type []uint8 into type *time.Time +is this a known bug ? or any specific mysql version which is required (running 5.7) ? + +``` +$ hydra help host +... + - MySQL: If DATABASE_URL is a DSN starting with mysql:// MySQL will be used as storage backend. + Example: DATABASE_URL=mysql://user:password@tcp(host:123)/database?parseTime=true + + Be aware that the ?parseTime=true parameter is mandatory, or timestamps will not work. +... +``` + +## The docker image exits immediately + +Check the logs using `docker logs `. + +## Insufficient Entropy + +> Hey there , I am getting this error when I try request an access token "The request used a security parameter (e.g., anti-replay, anti-csrf) with insufficient entropy (minimum of 8 characters)" + +> Kareem Diaa @kimooz Jun 07 16:41 +Hey there , I am getting this error when I try request an access token "The request used a security parameter (e.g., anti-replay, anti-csrf) with insufficient entropy (minimum of 8 characters)" + +> Aeneas @arekkas Jun 07 16:41 +@kimooz make sure state and nonce are set in your auth code url (http://hydra/oauth2/auth?client_id=...&nonce=THIS_NEEDS_TO_BE_SET&state=THIS_ALSO_NEEDS_TO_BE_SET + +## I get compile errors! + +> I would try deleting the vendor dir and glide’s files and try glide init again or clear Glide’s global cache. + +> follow the steps in the readme https://github.com/ory/hydra#building-from-source + +## Refreshing tokens + +> Kareem Diaa @kimooz 15:48 +One last question if you don't mind +from your experience do you think that saving the user access token in a session and validating it from the client on ever refresh does that make sense or not? +using the introspect endpoint + +> Aeneas @arekkas 15:51 +nah, simply write your http calls in a way that if a 401 or 403 occurrs, the token is refreshed +that's the easiest +and cleanest diff --git a/docs/hydra/index.md b/docs/hydra/index.md new file mode 100644 index 000000000..010ddbd10 --- /dev/null +++ b/docs/hydra/index.md @@ -0,0 +1,198 @@ +--- +id: hydra-index +title: Introduction +--- + +Welcome to the Hydra documentation. This documentation will + +1. teach you what OAuth2 and OpenID Connect are and how ORY Hydra fits in the picture. +2. help you run a ORY Hydra installation on your system using Docker. +3. teach you how to install, configure, run and use ORY Hydra. + +Let us begin with the first part, understanding what OAuth2 and OpenID Connect are. + +## What is ORY Hydra? + +ORY Hydra is an OAuth 2.0 and OpenID Connect Provider. As such, it is capable of issuing access, refresh, and ID Tokens. +Contrary to other projects out there, ORY Hydra does not offer user management (login, logout, profile management, +registration) but instead uses a redirection-based flow and a REST API to delegate user authentication (login) to +a service which you implement and control. This allows you to build a user management that works for you, with the frontend +technology that you like, and authentication mechanisms required by your use case (e.g. token-based 2FA, SMS 2FA). + +As such, ORY Hydra is the most flexible OAuth 2.0 and OpenID Connect provider out there and gives you great freedom +in implementing your business logic, and still getting all the benefits from OAuth 2.0 and OpenID Connect. + +Additional to the OAuth 2.0 functionality, ORY Hydra offers a safe storage for cryptographic keys (e.g. for signing JSON Web Tokens) +and is capable of managing OAuth 2.0 Clients. + +ORY Hydra is OpenID Connect certified (pending) and implements all the requirements stated by the OpenID Foundation. As such, +it correctly implements the different OAuth 2.0 and OpenID Connect flows as intended by the IETF and OpenID Foundation. + +## Introduction to OAuth 2.0 and OpenID Connect + +This section will give you some ideas of what OAuth 2.0 and OpenID Connect 1.0 are for. If you +already know what OAuth2 and OpenID Connect are and how they work, you can skip to the next [Section](#introduction-to-hydra). +This section will not explain how the various flows of OAuth2 work and how they look like. We strongly recommend +to read the following articles: + +* [DigitalOcean: An Introduction to OAuth 2](https://www.digitalocean.com/community/tutorials/an-introduction-to-oauth-2) +* [Aaron Parecki: OAuth2 Simplified](https://aaronparecki.com/2012/07/29/2/oauth2-simplified) +* [Zapier: Chapter 5: Authentication, Part 2](https://zapier.com/learn/apis/chapter-5-authentication-part-2/) + +Please be aware that we do not aim to explain OAuth 2.0 in great detail. We assume that you have some knowledge +of the flows and strongly encourage you to refresh your knowledge with the articles listed above. + +### What is OAuth 2.0? + +[The OAuth 2.0 authorization framework](https://tools.ietf.org/html/rfc6749) is a memo in the +[Request for Comments](https://www.ietf.org/rfc.html) document series published by the +IETF Internet Engineering Task Force (IETF). Memos in the Requests for Comments (RFC) document series +contain technical and organizational notes about the Internet. They cover many aspects of computer +networking, including protocols, procedures, programs, and concepts [...]. + +The OAuth 2.0 authorization framework enables a third-party +application to obtain limited access to an HTTP service, either on +behalf of a resource owner by orchestrating an approval interaction +between the resource owner and the HTTP service, or by allowing the +third-party application to obtain access on its own behalf. + +In the traditional client-server authentication model, the client +requests an access-restricted resource (protected resource) on the +server by authenticating with the server using the resource owner's +credentials. In order to provide third-party applications access to +restricted resources, the resource owner shares its credentials with +the third party. This creates several problems and limitations. + +OAuth addresses these issues by introducing an authorization layer +and separating the role of the client from that of the resource +owner. In OAuth, the client requests access to resources controlled +by the resource owner and hosted by the resource server, and is +issued a different set of credentials than those of the resource +owner. + +Instead of using the resource owner's credentials to access protected +resources, the client obtains an access token -- a string denoting a +specific scope, lifetime, and other access attributes. Access tokens +are issued to third-party clients by an authorization server with the +approval of the resource owner. The client uses the access token to +access the protected resources hosted by the resource server. + +Source: [IETF RFC 6749](https://tools.ietf.org/html/rfc6749) + +### OAuth 2.0 Example + +An end-user (resource owner) can grant a printing +service (client) access to her protected photos stored at a photo- +sharing service (resource server), without sharing her username and +password with the printing service. Instead, she authenticates +directly with a server trusted by the photo-sharing service +(authorization server), which issues the printing service delegation- +specific credentials (access token). + +Source: [IETF RFC 6749](https://tools.ietf.org/html/rfc6749) + +### What is OpenID Connect 1.0? + +OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. +It enables Clients to verify the identity of the End-User based on the authentication performed +by an Authorization Server, as well as to obtain basic profile information about the End-User in +an interoperable and REST-like manner. + +As background, the OAuth 2.0 Authorization Framework and OAuth 2.0 Bearer Token +Usage specifications provide a general framework for third-party +applications to obtain and use limited access to HTTP resources. +They define mechanisms to obtain and use Access Tokens to access resources +but do not define standard methods to provide identity information. +Notably, without profiling OAuth 2.0, it is incapable of providing information +about the authentication of an End-User. + +OpenID Connect implements authentication as an extension to the OAuth 2.0 authorization process. + +Source [OpenID Connect Core 1.0](http://openid.net/specs/openid-connect-core-1_0.html) + +OpenID Connect allows clients of all types, including Web-based, mobile, and JavaScript clients, +to request and receive information about authenticated sessions and end-users. The specification +suite is extensible, allowing participants to use optional features such as encryption of identity data, +discovery of OpenID Providers, and session management, when it makes sense for them. + +There are different work flows for OpenID Connect 1.0, we recommend checking out the OpenID Connect sandbox at +[openidconnect.net](https://openidconnect.net/). + +## Introduction to Hydra + +Hydra is a server implementation of the OAuth 2.0 authorization framework and the OpenID Connect Core 1.0. Existing OAuth2 +implementations usually ship as libraries or SDKs such as [node-oauth2-server](https://github.com/oauthjs/node-oauth2-server) +or [fosite](https://github.com/ory/fosite), or as fully featured identity solutions with user +management and user interfaces, such as [Keycloak](https://www.keycloak.org/) or [Okta](https://www.okta.com/). + +Implementing and using OAuth2 without understanding the whole specification is challenging and prone to errors, even when +SDKs are being used. The primary goal of Hydra is to make OAuth 2.0 and OpenID Connect 1.0 less painful to set up and easier to use. + +Hydra implements the flows described in OAuth2 and OpenID Connect 1.0 without forcing you to use a "Hydra User Management" +or some template engine or a predefined front-end. Instead it relies on HTTP redirection and cryptographic methods +to verify user consent allowing you to use Hydra with any authentication endpoint, be it [authboss](https://github.com/go-authboss/authboss), +[auth0.com](https://auth0.com/) or your proprietary PHP authentication. + +Hydra incorporates best practices in the area of the web service technology: + +1. Hydra ships as a single binary for all popular platforms including Linux, OSX and Windows, without any additional +dependencies. For further simplicity, Hydra is available as a [Docker Image](https://hub.docker.com/r/oryd/hydra/). +2. Hydra is built security first: architecture and work flows are designed to neutralize various common (OWASP TOP TEN) +and uncommon attack vectors. [Learn more](https://www.ory.sh/docs/guides/master/hydra/5-security/). +3. Hydra has a low CPU and memory footprint, short start up times and a CLI with developers in mind. +4. Hydra scales effortlessly up and down on every platform imaginable, including Heroku, Cloud Foundry, Docker, +Google Container Engine and many more. + +Hydra has a limitations too: + +1. Hydra is not something that manages user accounts. Hydra does not offer user registration, password reset, user +login, sending confirmation emails. This is what the *Identity Provider* is responsible for. +The communication between Hydra and the Identity Provider is called [*Login and Consent Flow*](./oauth2.md). +2. If you are building a simple service for 50-100 registered users, OAuth2 and Hydra will probably be too sophisticated. +3. Hydra will not support the OAuth2 resource owner password credentials flow. This flow is legacy, discouraged, +and insecure. + +OAuth2 is used in many areas, for various purposes and supported by all well known programming languages, but it is important +to understand what the vision of OAuth2 is. This non-exclusive list might help you decide, if OAuth 2.0 and Hydra are +the right fit for you. + +1. If you want to allow third-party developers accessing your APIs now or in the future, Hydra is the perfect fit. This is what an OAuth2 Provider does. +2. If you want to become a Identity Provider, like Google, Facebook or Microsoft, OpenID Connect and thus Hydra is a perfect fit. +3. Running an OAuth2 Provider works great with browser, mobile and wearable apps, as you can avoid storing access +credentials on the device, phone or wearable and revoke access tokens, and thus access privileges, at any time. +4. If you have a lot of services and want to limit automated access (think: cronjobs) for those services, +OAuth2 might make sense for you. Example: The comment service is not allowed to read user passwords when fetching +the latest user profile updates. + +# OAuth 2.0 Case Study + +OAuth2 and OpenID Connect are tricky to understand. It is important to understand that OAuth2 is +a delegation protocol. It makes sense to use Hydra in new and existing projects. A use case covering an existing project +explains how one would use Hydra in a new one as well. So let's look at a use case! + +Let's assume we are running a ToDo List App (todo24.com). ToDo24 has a login endpoint (todo24.com/login). +The login endpoint is written in node and uses MongoDB to store user information (email + password + settings). Of course, +todo24 has other services as well: list management (todo24.com/lists/manage: close, create, move), +item management (todo24.com/lists/items/manage: mark solved, add), and so on. You are using cookie-based sessions to see which +user is performing the request. + +Now you decide to use OAuth2 on top of your current infrastructure. There are many reasons to do this: +* You want to open your APIs to third-party developers. Their apps will be using OAuth2 Access Tokens to access a user's to do list. +* You want more client applications, for example browser app (SPA), mobile app, car, ... +* You have Cross Origin Requests. Making cookies work with Cross Origin Requests weakens or even disables important anti-CSRF measures. + +These are only a couple of reasons to use OAuth2. You might decide to use OAuth2 as your single source of authorization, thus maintaining +only one authorization protocol and being able to open up to third party devs in no time. With OpenID Connect, you are able to delegate authentication as well as authorization! + +Your decision is final. You want to use OAuth2 and you want Hydra to do the job. You install Hydra in your cluster using docker. +Next, you set up some exemplary OAuth2 clients. Clients can act on their own, but most of the time they need to access a user's todo lists. +To do so, the client initiates an OAuth2 request. This is where the [user login & consent flow](./oauth2.md) comes into play. +Before Hydra can issue an access token, we need to know WHICH user is giving consent. To do so, Hydra redirects the user agent (e.g. browser, mobile device) +to the login endpoint alongside with a challenge that contains important request information. The login endpoint (todo24.com/login) authenticates the +user as usual, e.g. by username & password, session cookie or other means. Upon successful authentication, the login endpoint +redirects the user back to ORY Hydra. Next, ORY Hydra needs the user's consent for which the user agent is redirected to the consent endpoint (todo24.com/consent) +where the user is asked for consent: *"Do you want to grant MyCoolAnalyticsApp read & write access to all your todo lists? [Yes] [No]"*. Once the user clicks *Yes* and gives consent, +the consent endpoint redirects back to ORY Hydra which then validates the request and finally issues the access, refresh, and ID tokens. + +You can validate the access tokens which are sent to your API directly at ORY Hydra, or use an Identity & Access Proxy +like ORY Oathkeeper to do it for you. diff --git a/docs/hydra/integration.md b/docs/hydra/integration.md new file mode 100644 index 000000000..833204217 --- /dev/null +++ b/docs/hydra/integration.md @@ -0,0 +1,78 @@ +--- +id: hydra-integration +title: Integration +--- + +This article explains how you to integrate ORY Hydra in your system. + + + +## Overview + +A high-level overview of the interaction between a client, ORY Hydra (Authorization Server) and an API looks as follows: + +![`sequenceDiagram + participant Client + participant ORY Hydra + participant API + Client->>ORY Hydra: Perform OAuth 2.0 Flow + ORY Hydra->>Client: Access Token + Client->>API: Request with Access Token + API->ORY Hydra: Validates Access Token + API->>Client: Response`](../../images/docs/hydra/basic-oauth2-system.png) + +Most of what is explained here can also be seen as real-life examples in the [ory/examples](https://github.com/ory/examples) +repository! + +### Interacting with OAuth 2.0 + +**Please, do not write your own code to interact with OAuth 2.0**. Use open source & battle-tested libraries instead. Here are some +examples: + +* NodeJS + * [passport](http://www.passportjs.org/) + * [simple-oauth2](https://github.com/lelylan/simple-oauth2) +* Golang + * [golang/oauth2](https://github.com/golang/oauth2) **recommended* +* PHP + * [oauth2-client](https://github.com/thephpleague/oauth2-client) +* Java + * [Sprint Security OAuth](https://spring.io/projects/spring-security-oauth) + +For a full list of client libraries go [here](https://oauth.net/code/). + +### Validating OAuth 2.0 Access Tokens + +The best and easiest way to validate OAuth 2.0 Access Tokens is by performing OAuth 2.0 Token Introspection. You can +do this with the CLI `hydra token introspect `. + +#### NodeJS + +``` +const token = 'the access token' +const body = qs.stringify({ token }) + +fetch('http://ory-hydra/oauth2/introspect', { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': body.length + }, + method: 'POST', body +}).then(body => { + if (!body.active) { + // Token is not active/valid + } else if (body.token_type !== 'access_token') { + // Token is not an access token (probably a refresh token) + } + + // token is active +}) +``` + +#### CURL + +```bash +$ curl -X POST \ + -d 'token=' \ + http://localhost:4445/oauth2/introspect +``` diff --git a/docs/hydra/jwks.md b/docs/hydra/jwks.md new file mode 100644 index 000000000..75677c79b --- /dev/null +++ b/docs/hydra/jwks.md @@ -0,0 +1,54 @@ +--- +id: hydra-jwks +title: JSON Web Key Sets +--- + +A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key and is +specified at [IETF RFC7517](https://tools.ietf.org/html/rfc7517). If you've heard of PEM files... + +``` +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDg +MBQGCCqGSIb3DQMHBAgD1kGN4ZslJgSCBMi1xk9jhlPxPc +9g73NQbtqZwI+9X5OhpSg/2ALxlCCjbqvzgSu8gfFZ4yo+ +A .... MANY LINES LIKE THAT .... +X0R+meOaudPTBxoSgCCM51poFgaqt4l6VlTN4FRpj+c/Wc +blK948UAda/bWVmZjXfY4Tztah0CuqlAldOQBzu8TwE7WD +H0ga/iLNvWYexG7FHLRiq5hTj0g9mUPEbeTXuPtOkTEb/0 +GEs= +-----END ENCRYPTED PRIVATE KEY----- +``` + +... JWKs are the same, but formatted using JSON: + +``` +{ + "keys": + [ + {"kty":"oct", + "alg":"A128KW", + "k":"GawgguFyGrWKav7AX4VKUg"}, + + {"kty":"oct", + "k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75 + aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow", + "kid":"HMAC key used in JWS spec Appendix A.1 example"} + ] +} +``` + +ORY Hydra offers an API for generating and managing JWKs, the [JSON Web Keys API](https://www.ory.sh/docs/api/hydra). +When using persistent storage backends, the keys are encrypted at rest using AES256-GCM and *the system secret*. +The system secret is generated by default and overridden by the environment variable `SYSTEM_SECRET`. + +JWKs are well supported amongst all languages. This endpoint helps you managing +certificates, private, public and symmetric keys. It is important to never transport keys over insecure channels such as http. + +The [REST API Documentation](https://www.ory.sh/docs/api/hydra/) will give you details on the various endpoints. + +## Auto-generated JWKs + +Hydra generates a couple of JSON Web Keys in order to operate correctly: + +* `http://localhost:4445/keys/hydra.openid.id-token`: A RSA public/private key pair for signing and validating OpenID Connect ID Tokens. +* `http://localhost:4445/keys/https-tls`: A RSA public/private key pair and a certificate for signing HTTP over TLS. diff --git a/docs/hydra/limitations.md b/docs/hydra/limitations.md new file mode 100644 index 000000000..34c763c93 --- /dev/null +++ b/docs/hydra/limitations.md @@ -0,0 +1,70 @@ +--- +id: hydra-limitations +title: Limitations +--- + +# Limitations + +ORY Hydra tries to solve all of OAuth 2.0 and OpenID Connect uses. There are, however, some limitations. + + + +## MySQL <= 5.6 / MariaDB + +ORY Hydra has issues with MySQL <= 5.6 (but not MySQL 5.7+) and certain MariaDB versions. Read more about this [here](https://github.com/ory/hydra/issues/377). +Our recommendation is to use MySQL 5.7+ or PostgreSQL. + +## Resource Owner Password Credentials Grant Type (ROCP) + +ORY Hydra does not and will not implement the Resource Owner Password Credentials Grant Type. Read on for context. + +### Overview + +This grant type allows OAuth 2.0 Clients to exchange user credentials (username, password) for an access token. + +**Request:** + +``` +POST /oauth2/token HTTP/1.1 +Host: server.example.com +Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW +Content-Type: application/x-www-form-urlencoded + +grant_type=password&username=johndoe&password=A3ddj3w +``` + +**Response:** + +``` +HTTP/1.1 200 OK +Content-Type: application/json;charset=UTF-8 +Cache-Control: no-store +Pragma: no-cache + +{ + "access_token":"2YotnFZFEjr1zCsicMWpAA", + "token_type":"example", + "expires_in":3600, + "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", + "example_parameter":"example_value" +} +``` + +You might think that this is the perfect grant type for your first-party application. This grant type is most commonly +used in mobile authentication for first-party apps. If you plan on doing this, stop right now and read +[this blog article](https://www.ory.sh/oauth2-for-mobile-app-spa-browser). + +### Legacy & Bad Security + +The ROCP grant type is discouraged by developers, professionals, and the IETF itself. It was originally added because +big legacy corporations (not dropping any names, but they are part of the IETF consortium) did not want to migrate their authentication +infrastructure to the modern web but instead do what they've been doing all along "but OAuth 2.0" and for systems that +want to upgrade from OAuth (1.0) to OAuth 2.0. + +There are a ton of good reasons why this is a bad flow, they are summarized in +[this excellent blog article as well](https://www.scottbrady91.com/OAuth/Why-the-Resource-Owner-Password-Credentials-Grant-Type-is-not-Authentication-nor-Suitable-for-Modern-Applications). + +### What about Auth0, Okta, ...? + +Auth0, Okta, Stormpath started early with OAuth 2.0 SaaS and adopted the ROPC grant too. They since deprecated these +old flows but still have them active as existing apps rely on them. diff --git a/docs/hydra/oauth2.md b/docs/hydra/oauth2.md new file mode 100644 index 000000000..5537e12b6 --- /dev/null +++ b/docs/hydra/oauth2.md @@ -0,0 +1,429 @@ +--- +id: hydra-oauth2 +title: OAuth 2.0 & Open ID Connect +--- + +Please read this chapter carefully, it is imperative to getting started and grasping all the concepts quickly. The +next sections will give you an overview of this chapter and explain some concepts. Do not skip the chapters. Seriously! :) + +The first important concept to understand is that ORY Hydra is an OAuth 2.0 Authorization and OpenID Connect Server. +Some mistake these capabilities for systems that store user data and log you in. This is not the case. Instead, such a +server is responsible for "translating" user credentials (typically username and password) to OAuth 2.0 Access and Refresh Tokens +as well as OpenID Connect ID Tokens. It's basically like you storing cookies with session data, but more flexible and it also +works for third party applications. + +ORY Hydra does not store user profiles, usernames, passwords. This capability is up to you. ORY Hydra uses something we +call a **User Login and Consent Flow**. This flow uses HTTP redirects to forward any incoming authorization request ("Please give +me an access token.") to the **Login Provider** and the **Consent Provider**. These applications are something you implement. +It can be a new app or your existing login system. On a high level, these providers can be summarized as: + +- The login provider is responsible for authenticating the user ("login") by validating his or her credentials (e.g. username + password). +- The consent provider is responsible for allowing the OAuth 2.0 application to get a token on the user's behalf ("Do you want +to allow foobar-app access to all your personal messages and images?". + +A second important concept is the OAuth 2.0 Scope. + +Often, developers confuse OAuth 2.0 Scope with regular Access Control. OAuth 2.0 Scope and, for example, Role Based Access +Control (RBAC) or Access Control Lists (ACL) cover different aspects of access control. + +Your internal access control will tell you what a user can do in your system. An administrator might modify everything, +a regular user might only be allowed to read personal messages. The OAuth 2.0 Scope does not represent what a resource owner ("user") is able to +do in a system or not. They do not express things like administrative rights. + +The OAuth 2.0 Scope expresses what a user allowed an OAuth 2.0 Client (read: "access token") to do on his/her behalf. +For example, an access token might be allowed to see a user's pictures, but not upload new pictures on his/her behalf. +The user him/herself however is generally allowed to view and upload pictures. **The OAuth 2.0 Scope do not express a user's permissions.** +They express what an OAuth 2.0 Client may do on the user's behalf - independently of whether or not the user is actually allowed +to do that. For example, the user could lie and say that the client is allowed to access some protected resource which +he does not have access to ("Read all classified documents", but he is not allowed to view any classified documents). + +This concludes the overview of the two most important pieces of ORY Hydra. + +To recap, ORY Hydra's primary feature is implementing the OAuth 2.0 and OpenID Connect spec, as well as related specs by the IETF +and OpenID Foundation. + +The next sections explain how to connect your existing user management (user login, registration, logout, ...) with ORY Hydra +in order to become an OAuth 2.0 and OpenID Connect provider like Google, Dropbox, or Facebook. + +Again, please be aware that you must know how OAuth 2.0 and OpenID Connect work. This documentation will not teach you how +these protocols work. + +## Glossary + +Before we get into the gritty details of how everything fits together, let's get some terminologies out of the way. You will +find these terminologies scattered across the OAuth2 and OpenID Connect ecosystem. + +We decided, for this guide, to use simpler and easier to use terminologies like, for example, *user* instead of *resource owner*. +If you are familiar with OAuth2 details, you will find it easier to navigate these docs if you have read the glossary. + +1. The **resource owner** is the user who authorizes an application to access their account. The application's access to +the user's account is limited to the "scope" of the authorization granted (e.g. read or write access). We will refer to +the resource owner as a *user* or *end user* on this page. +2. The **OAuth 2.0 Authorization Server** implements the OAuth 2.0 protocol (and optionally OpenID Connect) and serves +endpoints such as `/oauth2/auth` or `/oauth2/token`. In our case, this is **ORY Hydra**. +3. The **resource provider** is a service that - well - provides resources. These resources (e.g. a blog article, printer, todo list) +are owned by a resource owner (user) mentioned above. +3. The **OAuth 2.0 Client** is the *application* that wants access to a resource owner's resources (a.k.a. get write access to a user's images). +Such a client can ask the authorization server to issue an access token on a resource owner's behalf. Typically, the authorization server +will ask the user if he/she "is ok with" giving that application e.g. write access to personal images. +4. The **Identity Provider** is a service ("application"/"website") with a login interface. An identity provider typically +allows users to register as well and might also have an administrative interface in order to manage the identities (delete user, ban user, create user, ...). +5. **User Agent** is usually a browser. +6. **OpenID Connect** is a protocol built on top of OAuth 2.0 which is capable of federating authentication. + +A typical OAuth 2.0 flow looks as follows: + +1. A developer registers an OAuth 2.0 Client at the Authorization Server (ORY Hydra) with the intention of obtaining information on behalf of a user. +2. The application UI asks the user to authorize the application to access information/data on his/her behalf. +3. The user is redirected to the Authorization Server. +4. The Authorization Server confirms the user's identity and asks the user to grant the OAuth 2.0 Client certain permissions. +5. The Authorization Server issues tokens that the OAuth 2.0 client uses to access resources on the user's behalf. + +## Authenticating Users and Requesting Consent + +As you already know by now, ORY Hydra does not come with any type of user management (login, registration, ...). +Instead, it relies on the so-called User Login and Consent Flow. This flow describes a series of redirects where the user's +user agent is redirect to your Login Provider and, once the user is authenticated, to the Consent Provider. The Login +and Consent provider is implemented by you in a programming language of your choice. You could write, for example, a +NodeJS app that handles HTTP requests to `/login` and `/consent` and it would thus be your Login & Consent provider. + +The flow itself works as follows: + +1. The OAuth 2.0 Client initiates an Authorize Code, Hybrid, or Implicit flow. The user's user agent is redirect to +`http://hydra/oauth2/auth?client_id=...&...`. +2. ORY Hydra, if unable to authenticate the user (= no session cookie exists), redirects the user's user agent to the Login Provider +URL. The application "sitting" at that URL is implemented by you and typically shows a login user interface ("Please enter +your username and password"). The URL the user is redirect to looks similar to `http://login-service/login?login_challenge=1234...`. +3. The Login Provider, once the user has successfully logged in, tells ORY Hydra some information about who the user is (e.g. the user's ID) +and also that the login attempt was successful. This is done using a REST request which includes another redirect URL +along the lines of `http://hydra/oauth2/auth?client_id=...&...&login_verifier=4321`. +4. The user's user agent follows the redirect and lands back at ORY Hydra. Next, ORY Hydra redirects the user's user +agent to the Consent Provider, hosted at - for example - `http://consent-service/consent?consent_challenge=4567...` +5. The Consent Provider shows a user interface which asks the user if he/she would like to grant the OAuth 2.0 Client +the requested permissions ("OAuth 2.0 Scope"). You've probably seen this screen around, which is usually something similar to: +*"Would you like to grant Facebook Image Backup access to all your private and public images?"*. +6. The Consent Provider makes another REST request to ORY Hydra to let it know which permissions the user authorized, and +if the user authorized the request at all. The user can usually choose to not grant an application any access to his/her +personal data. In the response of that REST request, a redirect URL is included along the lines of `http://hydra/oauth2/auth?client_id=...&...&consent_verifier=7654...`. +7. The user's user agent follows that redirect. +7. Now, the user has successfully authenticated and authorized the application. Next, ORY Hydra will +run some checks and if everything works out, issue access, refresh, and ID tokens. + +This flow allows you to take full control of the behaviour of your login system (e.g. 2FA, passwordless, ...) and +consent screen. A well-documented reference implementation for both the Login and [Consent Provider is available on GitHub](https://github.com/ory/hydra-login-consent-node). + +### The flow from a user's point of view + + + +### The flow from a network perspective + +![https://mermaidjs.github.io/mermaid-live-editor/#/edit/eyJjb2RlIjoic2VxdWVuY2VEaWFncmFtXG4gICAgT0F1dGgyIENsaWVudC0-Pk9SWSBIeWRyYTogSW5pdGlhdGVzIE9BdXRoMiBBdXRob3JpemUgQ29kZSBvciBJbXBsaWNpdCBGbG93XG4gICAgT1JZIEh5ZHJhLS0-Pk9SWSBIeWRyYTogTm8gZW5kIHVzZXIgc2Vzc2lvbiBhdmFpbGFibGUgKG5vdCBhdXRoZW50aWNhdGVkKVxuICAgIE9SWSBIeWRyYS0-PkxvZ2luIFByb3ZpZGVyOiBSZWRpcmVjdHMgZW5kIHVzZXIgd2l0aCBsb2dpbiBjaGFsbGVuZ2VcbiAgICBMb2dpbiBQcm92aWRlci0tPk9SWSBIeWRyYTogRmV0Y2hlcyBsb2dpbiBpbmZvXG4gICAgTG9naW4gUHJvdmlkZXItLT4-TG9naW4gUHJvdmlkZXI6IEF1dGhlbnRpY2F0ZXMgdXNlciB3aXRoIGNyZWRlbnRpYWxzXG4gICAgTG9naW4gUHJvdmlkZXItLT5PUlkgSHlkcmE6IFRyYW5zbWl0cyBsb2dpbiBpbmZvIGFuZCByZWNlaXZlcyByZWRpcmVjdCB1cmwgd2l0aCBsb2dpbiB2ZXJpZmllclxuICAgIExvZ2luIFByb3ZpZGVyLT4-T1JZIEh5ZHJhOiBSZWRpcmVjdHMgZW5kIHVzZXIgdG8gcmVkaXJlY3QgdXJsIHdpdGggbG9naW4gdmVyaWZpZXJcbiAgICBPUlkgSHlkcmEtLT4-T1JZIEh5ZHJhOiBGaXJzdCB0aW1lIHRoYXQgY2xpZW50IGFza3MgdXNlciBmb3IgcGVybWlzc2lvbnNcbiAgICBPUlkgSHlkcmEtPj5Db25zZW50IFByb3ZpZGVyOiBSZWRpcmVjdHMgZW5kIHVzZXIgd2l0aCBjb25zZW50IGNoYWxsZW5nZVxuICAgIENvbnNlbnQgUHJvdmlkZXItLT5PUlkgSHlkcmE6IEZldGNoZXMgY29uc2VudCBpbmZvICh3aGljaCB1c2VyLCB3aGF0IGFwcCwgd2hhdCBzY29wZXMpXG4gICAgQ29uc2VudCBQcm92aWRlci0tPj5Db25zZW50IFByb3ZpZGVyOiBBc2tzIGZvciBlbmQgdXNlcidzIHBlcm1pc3Npb24gdG8gZ3JhbnQgYXBwbGljYXRpb24gYWNjZXNzXG4gICAgQ29uc2VudCBQcm92aWRlci0tPk9SWSBIeWRyYTogVHJhbnNtaXRzIGNvbnNlbnQgcmVzdWx0IGFuZCByZWNlaXZlcyByZWRpcmVjdCB1cmwgd2l0aCBjb25zZW50IHZlcmlmaWVyXG4gICAgQ29uc2VudCBQcm92aWRlci0-Pk9SWSBIeWRyYTogUmVkaXJlY3RzIHRvIHJlZGlyZWN0IHVybCB3aXRoIGNvbnNlbnQgdmVyaWZpZXJcbiAgICBPUlkgSHlkcmEtLT4-T1JZIEh5ZHJhOiBWZXJpZmllcyBncmFudFxuICAgIE9SWSBIeWRyYS0-Pk9BdXRoMiBDbGllbnQ6IFRyYW5zbWl0cyBhdXRob3JpemF0aW9uIGNvZGUvdG9rZW4iLCJtZXJtYWlkIjp7InRoZW1lIjoiZGVmYXVsdCJ9fQ](../../images/docs/hydra/login-consent-flow.png) + +### Implementing a Login & Consent Provider + +You should now have a high-level idea of how the login and consent providers work. Let's get into the details of it. + +#### OAuth 2.0 Authorize Code Flow + +Before anything happens, the OAuth 2.0 Authorize Code Flow is initiated by an OAuth 2.0 Client. This usually works by +generating a URL in the form of `https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&...`. Then, +the OAuth 2.0 Client points the end user's user agent to that URL. + +Next, the user agent (browser) opens that URL. + +#### User Login + +As the user agent hits the URL, ORY Hydra checks if a session cookie is set containing information about a previously +successful login. Additionally, parameters such as `id_token_hint`, `prompt`, and `max_age` are evaluated and processed. + +Next, the user will be redirect to the Login Provider which was set using the `OAUTH2_LOGIN_URL` environment +variable. For example, the user is redirected to `https://login-provider/login?login_challenge=1234` if `OAUTH2_LOGIN_URL=https://login-provider/login`. +This redirection happens *always* and regardless of whether the user has a valid login session or if the user needs +to authenticate. + +The service which handles requests to `https://login-provider/login` must first fetch information on the authentication +request using a REST API call. Please be aware that for reasons of brevity, the following code snippets are pseudo-code. +For a fully working example, check out our reference [User Login & Consent Provider implementation](https://github.com/ory/hydra-login-consent-node). + +The endpoint handler at `/login` **must not remember previous sessions**. This task is solved by ORY Hydra. If the +REST API call tells you to show the login ui, you **must show it**. If the REST API tells you to not show the login ui, +**you must not show it**. Again, **do not implement any type of session here**. + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +router.get('/login', function (req, res, next) { + challenge = req.url.query.login_challenge; + + fetch('https://hydra/oauth2/auth/requests/login/' + challenge). + then(function (response) { + // ... + }) +}) +``` + +The server response is a JSON object with the following keys: + +``` +{ + // Skip, if true, let's us know that ORY Hydra has successfully authenticated the user and we should not show any UI + "skip": true|false, + + // The user-id of the already authenticated user - only set if skip is true + "subject": "user-id", + + // The OAuth 2.0 client that initiated the request + "client": {"id": "...", ...}, + + // The initial OAuth 2.0 request url + "request_url": "https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&...", + + // The OAuth 2.0 Scope requested by the client, + "requested_scope": ["foo", "bar"], + + // Information on the OpenID Connect request - only required to process if your UI should support these values. + "oidc_context": {"ui_locales": [...], ...} +} +``` + +For a full documentation on all available keys, please head over to the [API documentation](https://www.ory.sh/docs/api/hydra/) +(make sure to select the right API version). + +Depending of whether or not `skip` is true, you will prompt the user to log in by showing him/her a username/password form, +or by using some other proof of identity. + +If `skip` is true, you **should not** show a user interface but accept the login request directly by making a REST call. +You can use this step to update some internal count of how often a user logged in, or do some other custom business logic. +But again, do not show the user interface. + +To accept the login request, do something along the lines of: + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +const body = { + // This is the user ID of the user that authenticated. If `skip` is true, this must be the `subject` + // value from the `fetch('https://hydra/oauth2/auth/requests/login/' + challenge)` response: + // + // subject = response.subject + // + // Otherwise, this can be a value of your choosing: + subject: "...", + + // If remember is set to true, then the authentication session will be persisted in the user's browser by ORY Hydra. This will set the `skip` flag to true in future requests that are coming from this user. This value has no effect if `skip` was true. + remember: true|false, + + // The time (in seconds) that the cookie should be valid for. Only has an effect if `remember` is true. + remember_for: 3600, + + // This value is specified by OpenID connect and optional - it tells OpenID Connect which level of authentication the user performed - for example 2FA or using some biometric data. The concrete values are up to you here. + acr: ".." +} + +fetch('https://hydra/oauth2/auth/requests/login/' + challenge + '/accept', { + method: 'PUT', + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' } +}). + then(function (response) { + // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next. + res.redirect(response.redirect_to); + }) +``` + +You may also choose to deny the login request. This is possible regardless of the `skip` value. + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +const body = { + error: "...", // This is an error ID like `login_required` or `invalid_request` + error_description: "..." // This is a more detailed description of the error +} + +fetch('https://hydra/oauth2/auth/requests/login/' + challenge + '/reject', { + method: 'PUT', + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' } +}). + then(function (response) { + // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next. + res.redirect(response.redirect_to); + }) +``` + +#### User Consent + +Now that we know who the user is, we must ask the user if he/she wants to grant the requested permissions to the OAuth 2.0 Client. +To do so, we check if the user has previously granted that exact OAuth 2.0 Client the requested permissions. If the user +has never granted any permissions to the client, or the client requires new permissions not previously granted, the user +must visually confirm the request. + +This works very similar to the User Login Flow. +First, the user will be redirect to the Consent Provider which was set using the `OAUTH2_CONSENT_PROVIDER` environment +variable. For example, the user is redirected to `https://consent-provider/consent?consent_challenge=1234` if `OAUTH2_CONSENT_PROVIDER=https://consent-provider/consent`. +This redirection happens *always* and regardless of whether the user has a valid login session or if the user needs +to authorize the application or not. + +The service which handles requests to `https://consent-provider/consent` must first fetch information on the consent +request using a REST API call. Please be aware that for reasons of brevity, the following code snippets are pseudo-code. +For a fully working example, check out our reference [User Login & Consent Provider implementation](https://github.com/ory/hydra-login-consent-node). + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +challenge = req.url.query.consent_challenge; + +fetch('https://hydra/oauth2/auth/requests/consent/' + challenge). + then(function (response) { + // ... + }) +``` + +The server response is a JSON object with the following keys: + +``` +{ + // Skip, if true, let's us know that the client has previously been granted the requested permissions (scope) by the end-user + "skip": true|false, + + // The user-id of the user that will grant (or deny) the request + "subject": "user-id", + + // The OAuth 2.0 client that initiated the request + "client": {"id": "...", ...}, + + // The initial OAuth 2.0 request url + "request_url": "https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&...", + + // The OAuth 2.0 Scope requested by the client, + "requested_scope": ["foo", "bar"], + + // Information on the OpenID Connect request - only required to process if your UI should support these values. + "oidc_context": {"ui_locales": [...], ...} +} +``` + +If skip is true, you should not show any user interface to the user. Instead, you should accept (or deny) the consent request. +Typically, you will accept the request unless you have a very good reason to deny it (e.g. the OAuth 2.0 Client is banned). + +If skip is false and you show the consent screen, you should use the `requested_scope` array to display a list of permissions +which the user must grant (e.g. using a checkbox). Some people choose to always skip this step if the OAuth 2.0 Client +is a first-party client - meaning that the client is used by you or your developers in an internal application. + +Assuming the user accepts the consent request, the code looks very familiar to the User Login Flow. + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +const body = { + // A list of permissions the user granted to the OAuth 2.0 Client. This can be fewer permissions that initially requested, but are rarely more or other permissions than requested. + grant_scope: ["foo", "bar"], + + // If remember is set to true, then the consent response will be remembered for future requests. This will set the `skip` flag to true in future requests that are coming from this user for the granted permissions and that particular client. This value has no effect if `skip` was true. + remember: true|false, + + // The time (in seconds) that the cookie should be valid for. Only has an effect if `remember` is true. + remember_for: 3600, + + // The session allows you to set additional data in the access and ID tokens. + session: { + // Sets session data for the access and refresh token, as well as any future tokens issued by the + // refresh grant. Keep in mind that this data will be available to anyone performing OAuth 2.0 Challenge Introspection. + // If only your services can perform OAuth 2.0 Challenge Introspection, this is usually fine. But if third parties + // can access that endpoint as well, sensitive data from the session might be exposed to them. Use with care! + access_token: { ... }, + + // Sets session data for the OpenID Connect ID token. Keep in mind that the session'id payloads are readable + // by anyone that has access to the ID Challenge. Use with care! + id_token: { ... }, + } +} + +fetch('https://hydra/oauth2/auth/requests/consent/' + challenge + '/accept', { + method: 'PUT', + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' } +}). + then(function (response) { + // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next. + res.redirect(response.redirect_to); + }) +``` + +You may also choose to deny the consent request. This is possible regardless of the `skip` value. + +``` +// This is node-js pseudo code and will not work if you copy it 1:1 + +const body = { + // This is an error ID like `consent_required` or `invalid_request` + error: "...", + + // This is a more detailed description of the error + error_description: "..." +} + +fetch('https://hydra/oauth2/auth/requests/consent/' + challenge + '/reject', { + method: 'PUT', + body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' } +}). + then(function (response) { + // The response will contain a `redirect_to` key which contains the URL where the user's user agent must be redirected to next. + res.redirect(response.redirect_to); + }) +``` + +Once the user agent is redirected back, the OAuth 2.0 flow will be finalized. + +### Revoking consent and login sessions + +#### Login + +You can revoke login sessions. Revoking a login session will remove all of the user's cookies at ORY Hydra and will require +the user to re-authenticate when performing the next OAuth 2.0 Authorize Code Flow. Be aware that this option will +remove all cookies from all devices. + +Revoking the login sessions of a user is as easy as sending `DELETE to `/oauth2/auth/sessions/login/{user}`. + +#### Consent + +You can revoke a user's consent either on a per application basis or for all applications. Revoking the consent will +automatically revoke all related access and refresh tokens. + +Revoking all consent sessions of a user is as easy as sending `DELETE to `/oauth2/auth/sessions/consent/{user}`. + +Revoking the consent sessions of a user for a specific client is as easy as sending `DELETE to `/oauth2/auth/sessions/consent/{user}/{client}`. + +## OAuth 2.0 Scope + +The scope of an OAuth 2.0 scope defines the permission the token was granted by the user. For example, a specific +token might be allowed to access public pictures, but not private ones. The granted permissions are established during +the consent screen. + +Additionally, ORY Hydra has pre-defined OAuth 2.0 Scope values: + +* `offline` and `offline_access`: Include this scope if you wish to receive a refresh token +* `openid`: Include this scope if you wish to perform an OpenID Connect request. + +## OAuth2 Token Introspection + +OAuth2 Token Introspection is an [IETF](https://tools.ietf.org/html/rfc7662) standard. +It defines a method for a protected resource to query +an OAuth 2.0 authorization server to determine the active state of an +OAuth 2.0 token and to determine meta-information about this token. +OAuth 2.0 deployments can use this method to convey information about +the authorization context of the token from the authorization server +to the protected resource. + +You can find more details on this endpoint in the [ORY Hydra API Docs](https://www.ory.sh/docs/). You can also use +the CLI command `hydra token introspect `. + +## OAuth 2.0 Clients + +You can manage *OAuth 2.0 clients* using the cli or the HTTP REST API. + +* **CLI:** `hydra help clients` +* **REST:** Read the [API Docs](https://www.ory.sh/docs) diff --git a/docs/hydra/production.md b/docs/hydra/production.md new file mode 100644 index 000000000..0634fbc44 --- /dev/null +++ b/docs/hydra/production.md @@ -0,0 +1,108 @@ +--- +id: hydra-production +title: Production +--- + +This document summarizes things you will find useful when going to production. + +## Configuration + +All configuration of ORY Hydra is currently done via environment variables. Setting environment variables works +differently on each system, so we collected some to help you get started. + +### Linux / OSX + +``` +$ export MY_ENV_VAR=foo +$ hydra ... +# or +$ MY_ENV_VAR=foo hydra ... +``` + +### Windows + +#### Command Prompt + +``` +$ set MY_ENV_VAR=foo +$ hydra ... +``` + +#### Powershell + +``` +$ $env:MY_ENV_VAR="foo" +$ hydra ... +``` + +### Docker + +``` +$ docker run -e MY_ENV_VAR=foo oryd/hydra:... +``` + +## ORY Hydra behind an API Gateway + +Although ORY Hydra implements all Go best practices around running public-facing production http servers, we discourage running +ORY Hydra facing the public net directly. We strongly recommend running ORY Hydra behind an API gateway or a load balancer. +It is common to terminate TLS on the edge (gateway / load balancer) and use certificates provided by your infrastructure +provider (e.g. AWS CA) for last mile security. + +### TLS Termination + +You may also choose to set Hydra to HTTPS mode without actually accepting TLS connections. In that case, +all Hydra URLs are prefixed with `https://`, but the server is actually accepting http. This makes sense if you don't want +last mile security using TLS, and trust your network to properly handle internal traffic. To use this setting, check +for `HTTPS_ALLOW_TERMINATION_FROM` in `hydra help host`. + +With TLS termination enabled, ORY Hydra discards all requests unless: + +* The request is coming from a trusted IP address set by `HTTPS_ALLOW_TERMINATION_FROM` and the header `X-Forwarded-Proto` is set to `https`. +* The request goes to `/health/status` which does not require TLS termination and that is used to check the health of an instance. + +If you are unable to properly set up TLS Termination, you may want to set the `--dangerous-force-http` flag. But please be +aware that we discourage you from doing so and that you should know what you're doing. + +### Routing + +It is common to use a router, or API gateway, to route subdomains or paths to a specific service. For example, `https://myservice.com/hydra/` +is routed to `http://10.0.1.213:3912/` where `10.0.1.213` is the host running ORY Hydra. To compute the values for +the consent challenge, ORY Hydra uses the host and path headers from the HTTP request. Therefore, it is important +to set up your API Gateway in such a way, that it passes the public host (in this case `myservice.com`) and the path +without any prefix (in this case `hydra/`). If you use the Mashape Kong API gateway, you can achieve this by setting +`strip_request_path=true` and `preserve_host=true.` + +## Exposing Administrative and Public API Endpoints + +ORY Hydra exposes serves APIs via two ports: + +- Public port (default 4444) +- Administrative port (default 4445) + +The public port can and should be exposed to public internet traffic. That port handles requests to: + +* `./well-known/jwks.json` +* `./well-known/openid-configuration` +* `/oauth2/auth` +* `/oauth2/token` +* `/oauth2/revoke` +* `/oauth2/fallbacks/consent` +* `/oauth2/fallbacks/error` +* `/userinfo` + +The administrative port should not be exposed to public internet traffic. If you want to expose certain endpoints, such as the `/clients` endpoint for +OpenID Connect Dynamic Client Registry, you can do so but you need to properly secure these endpoints with an API Gateway or Authorization Proxy. +Administrative endpoints include: + +* All `/clients` endpoints. +* All `/jwks` endpoints. +* All `/health`, `/metrics`, `/version` endpoints. +* All `/oauth2/auth/requests` endpoints. +* Endpoint `/oauth2/introspect`. +* Endpoint `/oauth2/flush`. + +None of the administrative endpoints have any built-in access control. You can do simple `curl` or Postman requests to talk to them. + +We generally advise to run ORY Hydra with `hydra serve all` which listens on both ports in one process. If you wish to have more granular control over +each endpoint's settings (e.g. CORS), you can run `hydra serve admin` and `hydra serve public` separately. Please be aware that the `memory` backend +will not work in this mode. diff --git a/docs/hydra/sdk/api.md b/docs/hydra/sdk/api.md new file mode 100644 index 000000000..f15d79fa7 --- /dev/null +++ b/docs/hydra/sdk/api.md @@ -0,0 +1,9006 @@ +--- +title: REST API +id: hydra-sdk-api +--- + + + +Welcome to the ORY Hydra HTTP API documentation. You will find documentation for all HTTP APIs here. + +> You are viewing a REST API documentation. This documentation is auto-generated from a swagger specification which +itself is generated from annotations in the source files of the project. It is possible that this documentation includes +bugs and that code samples are incomplete or wrong. +> +> If you find issues in the respective documentation, please do not edit the +markdown files directly (as they are generated) but raise an issue on the project's GitHub instead. This documentation +will improve over time with your help! If you have ideas how to improve this part of the documentation, feel free to +share them in a [GitHub issue](https://github.com/ory/docs/issues/new) any time. + +## Authentication + +- HTTP Authentication, scheme: basic - OAuth 2.0 Authorization. - Flow: authorizationCode + - OAuth 2.0 Authorization URL = [/oauth2/auth](/oauth2/auth) + - OAuth 2.0 Token URL = [/oauth2/token](/oauth2/token) + - OAuth 2.0 Scope + |Scope|Scope Description| + |---|---| + |offline|A scope required when requesting refresh tokens| + |openid|Request an OpenID Connect ID Token| + + +## Public Endpoints + + + +### JSON Web Keys Discovery + +``` +GET /.well-known/jwks.json HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns JSON Web Keys to be used as public keys for verifying OpenID Connect ID Tokens and, +if enabled, OAuth 2.0 JWT Access Tokens. This endpoint can be used with client libraries like +[node-jwks-rsa](https://github.com/auth0/node-jwks-rsa) among others. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|JSONWebKeySet|[JSONWebKeySet](#schemajsonwebkeyset)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /.well-known/jwks.json \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/.well-known/jwks.json", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/.well-known/jwks.json', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/.well-known/jwks.json"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/.well-known/jwks.json', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/.well-known/jwks.json', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### OpenID Connect Discovery + +``` +GET /.well-known/openid-configuration HTTP/1.1 +Accept: application/json + +``` + +The well known endpoint an be used to retrieve information for OpenID Connect clients. We encourage you to not roll +your own OpenID Connect client but to use an OpenID Connect client library instead. You can learn more on this +flow at https://openid.net/specs/openid-connect-discovery-1_0.html + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|wellKnown|[wellKnown](#schemawellknown)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "authorization_endpoint": "https://playground.ory.sh/ory-hydra/public/oauth2/auth", + "claims_parameter_supported": true, + "claims_supported": [ + "string" + ], + "grant_types_supported": [ + "string" + ], + "id_token_signing_alg_values_supported": [ + "string" + ], + "issuer": "https://playground.ory.sh/ory-hydra/public/", + "jwks_uri": "https://playground.ory.sh/ory-hydra/public/.well-known/jwks.json", + "registration_endpoint": "https://playground.ory.sh/ory-hydra/admin/client", + "request_parameter_supported": true, + "request_uri_parameter_supported": true, + "require_request_uri_registration": true, + "response_modes_supported": [ + "string" + ], + "response_types_supported": [ + "string" + ], + "scopes_supported": [ + "string" + ], + "subject_types_supported": "public, pairwise", + "token_endpoint": "https://playground.ory.sh/ory-hydra/public/oauth2/token", + "token_endpoint_auth_methods_supported": [ + "string" + ], + "userinfo_endpoint": "string", + "userinfo_signing_alg_values_supported": [ + "string" + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /.well-known/openid-configuration \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/.well-known/openid-configuration", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/.well-known/openid-configuration', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/.well-known/openid-configuration"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/.well-known/openid-configuration', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/.well-known/openid-configuration', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### The OAuth 2.0 authorize endpoint + +``` +GET /oauth2/auth HTTP/1.1 +Accept: application/json + +``` + +This endpoint is not documented here because you should never use your own implementation to perform OAuth2 flows. +OAuth2 is a very popular protocol and a library for your programming language will exists. + +To learn more about this flow please refer to the specification: https://tools.ietf.org/html/rfc6749 + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|302|[Found](https://tools.ietf.org/html/rfc7231#section-6.4.3)|Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 401 response + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /oauth2/auth \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/oauth2/auth", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/oauth2/auth', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/oauth2/auth', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/oauth2/auth', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Revoke OAuth2 tokens + +``` +POST /oauth2/revoke HTTP/1.1 +Content-Type: application/x-www-form-urlencoded +Accept: application/json + +``` + +Revoking a token (both access and refresh) means that the tokens will be invalid. A revoked access token can no +longer be used to make access requests, and a revoked refresh token can no longer be used to refresh an access token. +Revoking a refresh token also invalidates the access token that was created with it. + +#### Request body + +```yaml +token: string + +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|object|false|none| +|» token|body|string|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 401 response + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /oauth2/revoke \ + -H 'Content-Type: application/x-www-form-urlencoded' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/x-www-form-urlencoded"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/oauth2/revoke", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "token": "string" +}'; +const headers = { + 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' +} + +fetch('/oauth2/revoke', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/revoke"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json' +} + +r = requests.post( + '/oauth2/revoke', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Accept' => 'application/json' +} + +result = RestClient.post '/oauth2/revoke', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### The OAuth 2.0 token endpoint + +``` +POST /oauth2/token HTTP/1.1 +Accept: application/json + +``` + +This endpoint is not documented here because you should never use your own implementation to perform OAuth2 flows. +OAuth2 is a very popular protocol and a library for your programming language will exists. + +To learn more about this flow please refer to the specification: https://tools.ietf.org/html/rfc6749 + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|oauthTokenResponse|[oauthTokenResponse](#schemaoauthtokenresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "access_token": "string", + "expires_in": 0, + "id_token": 0, + "refresh_token": "string", + "scope": 0, + "token_type": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /oauth2/token \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/oauth2/token", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/oauth2/token', { + method: 'POST', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/token"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.post( + '/oauth2/token', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.post '/oauth2/token', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### OpenID Connect Userinfo + +``` +GET /userinfo HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns the payload of the ID Token, including the idTokenExtra values, of the provided OAuth 2.0 access token. +The endpoint implements http://openid.net/specs/openid-connect-core-1_0.html#UserInfo . + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|userinfoResponse|[userinfoResponse](#schemauserinforesponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "birthdate": "string", + "email": "string", + "email_verified": true, + "family_name": "string", + "gender": "string", + "given_name": "string", + "locale": "string", + "middle_name": "string", + "name": "string", + "nickname": "string", + "phone_number": "string", + "phone_number_verified": true, + "picture": "string", + "preferred_username": "string", + "profile": "string", + "sub": "string", + "updated_at": 0, + "website": "string", + "zoneinfo": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /userinfo \ + -H 'Accept: application/json' \ -H 'Authorization: Bearer {access-token}' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + "Authorization": []string{"Bearer {access-token}"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/userinfo", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json', 'Authorization': 'Bearer {access-token}' +} + +fetch('/userinfo', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/userinfo"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json', + 'Authorization': 'Bearer {access-token}' +} + +r = requests.get( + '/userinfo', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json', + 'Authorization' => 'Bearer {access-token}' +} + +result = RestClient.get '/userinfo', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## Administrative Endpoints + + + +### List OAuth 2.0 Clients + +``` +GET /clients HTTP/1.1 +Accept: application/json + +``` + +This endpoint lists all clients in the database, and never returns client secrets. + +OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|limit|query|integer(int64)|false|The maximum amount of policies returned.| +|offset|query|integer(int64)|false|The offset from where to start looking.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A list of clients.|Inline| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + + +##### Response Schema + +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[[oAuth2Client](#schemaoauth2client)]|false|none|none| +|» Client represents an OAuth 2.0 Client.|[oAuth2Client](#schemaoauth2client)|false|none|none| +|»» allowed_cors_origins|[string]|false|none|AllowedCORSOrigins are one or more URLs (scheme://host[:port]) which are allowed to make CORS requests to the /oauth/token endpoint. If this array is empty, the sever's CORS origin configuration (`CORS_ALLOWED_ORIGINS`) will be used instead. If this array is set, the allowed origins are appended to the server's CORS origin configuration. Be aware that environment variable `CORS_ENABLED` MUST be set to `true` for this to work.| +|»» audience|[string]|false|none|Audience is a whitelist defining the audiences this client is allowed to request tokens for. An audience limits the applicability of an OAuth 2.0 Access Token to, for example, certain API endpoints. The value is a list of URLs. URLs MUST NOT contain whitespaces.| +|»» client_id|string|false|none|ClientID is the id for this client.| +|»» client_name|string|false|none|Name is the human-readable string name of the client to be presented to the end-user during authorization.| +|»» client_secret|string|false|none|Secret is the client's secret. The secret will be included in the create request as cleartext, and then never again. The secret is stored using BCrypt so it is impossible to recover it. Tell your users that they need to write the secret down as it will not be made available again.| +|»» client_secret_expires_at|integer(int64)|false|none|SecretExpiresAt is an integer holding the time at which the client secret will expire or 0 if it will not expire. The time is represented as the number of seconds from 1970-01-01T00:00:00Z as measured in UTC until the date/time of expiration.| +|»» client_uri|string|false|none|ClientURI is an URL string of a web page providing information about the client. If present, the server SHOULD display this URL to the end-user in a clickable fashion.| +|»» contacts|[string]|false|none|Contacts is a array of strings representing ways to contact people responsible for this client, typically email addresses.| +|»» grant_types|[string]|false|none|GrantTypes is an array of grant types the client is allowed to use.| +|»» jwks|[JSONWebKeySet](#schemajsonwebkeyset)|false|none|none| +|»»» keys|[[JSONWebKey](#schemajsonwebkey)]|false|none|The value of the "keys" parameter is an array of JWK values. By default, the order of the JWK values within the array does not imply an order of preference among them, although applications of JWK Sets can choose to assign a meaning to the order for their purposes, if desired.| +|»»»» alg|string|true|none|The "alg" (algorithm) parameter identifies the algorithm intended for use with the key. The values used should either be registered in the IANA "JSON Web Signature and Encryption Algorithms" registry established by [JWA] or be a value that contains a Collision- Resistant Name.| +|»»»» crv|string|false|none|none| +|»»»» d|string|false|none|none| +|»»»» dp|string|false|none|none| +|»»»» dq|string|false|none|none| +|»»»» e|string|false|none|none| +|»»»» k|string|false|none|none| +|»»»» kid|string|true|none|The "kid" (key ID) parameter is used to match a specific key. This is used, for instance, to choose among a set of keys within a JWK Set during key rollover. The structure of the "kid" value is unspecified. When "kid" values are used within a JWK Set, different keys within the JWK Set SHOULD use distinct "kid" values. (One example in which different keys might use the same "kid" value is if they have different "kty" (key type) values but are considered to be equivalent alternatives by the application using them.) The "kid" value is a case-sensitive string.| +|»»»» kty|string|true|none|The "kty" (key type) parameter identifies the cryptographic algorithm family used with the key, such as "RSA" or "EC". "kty" values should either be registered in the IANA "JSON Web Key Types" registry established by [JWA] or be a value that contains a Collision- Resistant Name. The "kty" value is a case-sensitive string.| +|»»»» n|string|false|none|none| +|»»»» p|string|false|none|none| +|»»»» q|string|false|none|none| +|»»»» qi|string|false|none|none| +|»»»» use|string|true|none|Use ("public key use") identifies the intended use of the public key. The "use" parameter is employed to indicate whether a public key is used for encrypting data or verifying the signature on data. Values are commonly "sig" (signature) or "enc" (encryption).| +|»»»» x|string|false|none|none| +|»»»» x5c|[string]|false|none|The "x5c" (X.509 certificate chain) parameter contains a chain of one or more PKIX certificates [RFC5280]. The certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (Section 4 of [RFC4648] -- not base64url-encoded) DER [ITU.X690.1994] PKIX certificate value. The PKIX certificate containing the key value MUST be the first certificate.| +|»»»» y|string|false|none|none| +|»»» jwks_uri|string|false|none|URL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains the signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the Client's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing and encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both signatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.| +|»»» logo_uri|string|false|none|LogoURI is an URL string that references a logo for the client.| +|»»» owner|string|false|none|Owner is a string identifying the owner of the OAuth 2.0 Client.| +|»»» policy_uri|string|false|none|PolicyURI is a URL string that points to a human-readable privacy policy document that describes how the deployment organization collects, uses, retains, and discloses personal data.| +|»»» redirect_uris|[string]|false|none|RedirectURIs is an array of allowed redirect urls for the client, for example http://mydomain/oauth/callback .| +|»»» request_object_signing_alg|string|false|none|JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm.| +|»»» request_uris|[string]|false|none|Array of request_uri values that are pre-registered by the RP for use at the OP. Servers MAY cache the contents of the files referenced by these URIs and not retrieve them at the time they are used in a request. OPs can require that request_uri values used be pre-registered with the require_request_uri_registration discovery parameter.| +|»»» response_types|[string]|false|none|ResponseTypes is an array of the OAuth 2.0 response type strings that the client can use at the authorization endpoint.| +|»»» scope|string|false|none|Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens.| +|»»» sector_identifier_uri|string|false|none|URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values.| +|»»» subject_type|string|false|none|SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`.| +|»»» token_endpoint_auth_method|string|false|none|Requested Client Authentication method for the Token Endpoint. The options are client_secret_post, client_secret_basic, private_key_jwt, and none.| +|»»» tos_uri|string|false|none|TermsOfServiceURI is a URL string that points to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client.| +|»»» userinfo_signed_response_alg|string|false|none|JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims as a UTF-8 encoded JSON object using the application/json content-type.| + +##### Examples + +###### 200 response + +```json +[ + { + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" + } +] +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /clients \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/clients", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/clients', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/clients"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/clients', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/clients', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Create an OAuth 2.0 client + +``` +POST /clients HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Create a new OAuth 2.0 client If you pass `client_secret` the secret will be used, otherwise a random secret will be generated. The secret will be returned in the response and you will not be able to retrieve it later on. Write the secret down and keep it somwhere safe. + +OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components. + +#### Request body + +```json +{ + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[oAuth2Client](#schemaoauth2client)|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|oAuth2Client|[oAuth2Client](#schemaoauth2client)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /clients \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/clients", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/clients', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/clients"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/clients', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/clients', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Get an OAuth 2.0 Client. + +``` +GET /clients/{id} HTTP/1.1 +Accept: application/json + +``` + +Get an OAUth 2.0 client by its ID. This endpoint never returns passwords. + +OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the OAuth 2.0 Client.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|oAuth2Client|[oAuth2Client](#schemaoauth2client)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /clients/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/clients/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/clients/{id}', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/clients/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/clients/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/clients/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Update an OAuth 2.0 Client + +``` +PUT /clients/{id} HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Update an existing OAuth 2.0 Client. If you pass `client_secret` the secret will be updated and returned via the API. This is the only time you will be able to retrieve the client secret, so write it down and keep it safe. + +OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components. + +#### Request body + +```json +{ + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|none| +|body|body|[oAuth2Client](#schemaoauth2client)|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|oAuth2Client|[oAuth2Client](#schemaoauth2client)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /clients/{id} \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/clients/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/clients/{id}', { + method: 'PUT', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/clients/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.put( + '/clients/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.put '/clients/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Deletes an OAuth 2.0 Client + +``` +DELETE /clients/{id} HTTP/1.1 +Accept: application/json + +``` + +Delete an existing OAuth 2.0 Client by its ID. + +OAuth 2.0 clients are used to perform OAuth 2.0 and OpenID Connect flows. Usually, OAuth 2.0 clients are generated for applications which want to consume your OAuth 2.0 or OpenID Connect capabilities. To manage ORY Hydra, you will need an OAuth 2.0 Client as well. Make sure that this endpoint is well protected and only callable by first-party components. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the OAuth 2.0 Client.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 401 response + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /clients/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/clients/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/clients/{id}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/clients/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/clients/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/clients/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Retrieve a JSON Web Key Set + +``` +GET /keys/{set} HTTP/1.1 +Accept: application/json + +``` + +This endpoint can be used to retrieve JWK Sets stored in ORY Hydra. + +A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|set|path|string|true|The set| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|JSONWebKeySet|[JSONWebKeySet](#schemajsonwebkeyset)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /keys/{set} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/keys/{set}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/keys/{set}', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/keys/{set}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/keys/{set}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/keys/{set}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Update a JSON Web Key Set + +``` +PUT /keys/{set} HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Use this method if you do not want to let Hydra generate the JWKs for you, but instead save your own. + +A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well. + +#### Request body + +```json +{ + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|set|path|string|true|The set| +|body|body|[JSONWebKeySet](#schemajsonwebkeyset)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|JSONWebKeySet|[JSONWebKeySet](#schemajsonwebkeyset)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /keys/{set} \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/keys/{set}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/keys/{set}', { + method: 'PUT', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/keys/{set}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.put( + '/keys/{set}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.put '/keys/{set}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Generate a new JSON Web Key + +``` +POST /keys/{set} HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +This endpoint is capable of generating JSON Web Key Sets for you. There a different strategies available, such as symmetric cryptographic keys (HS256, HS512) and asymetric cryptographic keys (RS256, ECDSA). If the specified JSON Web Key Set does not exist, it will be created. + +A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well. + +#### Request body + +```json +{ + "alg": "string", + "kid": "string", + "use": "string" +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|set|path|string|true|The set| +|body|body|[jsonWebKeySetGeneratorRequest](#schemajsonwebkeysetgeneratorrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|JSONWebKeySet|[JSONWebKeySet](#schemajsonwebkeyset)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /keys/{set} \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/keys/{set}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "alg": "string", + "kid": "string", + "use": "string" +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/keys/{set}', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/keys/{set}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/keys/{set}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/keys/{set}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Delete a JSON Web Key Set + +``` +DELETE /keys/{set} HTTP/1.1 +Accept: application/json + +``` + +Use this endpoint to delete a complete JSON Web Key Set and all the keys in that set. + +A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|set|path|string|true|The set| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 401 response + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /keys/{set} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/keys/{set}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/keys/{set}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/keys/{set}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/keys/{set}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/keys/{set}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Fetch a JSON Web Key + +``` +GET /keys/{set}/{kid} HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns a singular JSON Web Key, identified by the set and the specific key ID (kid). + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|kid|path|string|true|The kid of the desired key| +|set|path|string|true|The set| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|JSONWebKeySet|[JSONWebKeySet](#schemajsonwebkeyset)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /keys/{set}/{kid} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/keys/{set}/{kid}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/keys/{set}/{kid}', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/keys/{set}/{kid}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/keys/{set}/{kid}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/keys/{set}/{kid}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Update a JSON Web Key + +``` +PUT /keys/{set}/{kid} HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Use this method if you do not want to let Hydra generate the JWKs for you, but instead save your own. + +A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well. + +#### Request body + +```json +{ + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|kid|path|string|true|The kid of the desired key| +|set|path|string|true|The set| +|body|body|[JSONWebKey](#schemajsonwebkey)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|JSONWebKey|[JSONWebKey](#schemajsonwebkey)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /keys/{set}/{kid} \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/keys/{set}/{kid}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/keys/{set}/{kid}', { + method: 'PUT', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/keys/{set}/{kid}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.put( + '/keys/{set}/{kid}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.put '/keys/{set}/{kid}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Delete a JSON Web Key + +``` +DELETE /keys/{set}/{kid} HTTP/1.1 +Accept: application/json + +``` + +Use this endpoint to delete a single JSON Web Key. + +A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that represents a cryptographic key. A JWK Set is a JSON data structure that represents a set of JWKs. A JSON Web Key is identified by its set and key id. ORY Hydra uses this functionality to store cryptographic keys used for TLS and JSON Web Tokens (such as OpenID Connect ID tokens), and allows storing user-defined keys as well. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|kid|path|string|true|The kid of the desired key| +|set|path|string|true|The set| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 401 response + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /keys/{set}/{kid} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/keys/{set}/{kid}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/keys/{set}/{kid}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/keys/{set}/{kid}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/keys/{set}/{kid}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/keys/{set}/{kid}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Get consent request information + +``` +GET /oauth2/auth/requests/consent/{challenge} HTTP/1.1 +Accept: application/json + +``` + +When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider +to authenticate the user and then tell ORY Hydra now about it. If the user authenticated, he/she must now be asked if +the OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the user's behalf. + +The consent provider which handles this request and is a web app implemented and hosted by you. It shows a user interface which asks the user to +grant or deny the client access to the requested scope ("Application my-dropbox-app wants write access to all your private files"). + +The consent challenge is appended to the consent provider's URL to which the user's user-agent (browser) is redirected to. The consent +provider uses that challenge to fetch information on the OAuth2 request and then tells ORY Hydra if the user accepted +or rejected the request. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|challenge|path|string|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|consentRequest|[consentRequest](#schemaconsentrequest)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "acr": "string", + "challenge": "string", + "client": { + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" + }, + "login_challenge": "string", + "login_session_id": "string", + "oidc_context": { + "acr_values": [ + "string" + ], + "display": "string", + "id_token_hint_claims": { + "property1": {}, + "property2": {} + }, + "login_hint": "string", + "ui_locales": [ + "string" + ] + }, + "request_url": "string", + "requested_access_token_audience": [ + "string" + ], + "requested_scope": [ + "string" + ], + "skip": true, + "subject": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /oauth2/auth/requests/consent/{challenge} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/oauth2/auth/requests/consent/{challenge}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/oauth2/auth/requests/consent/{challenge}', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/requests/consent/{challenge}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/oauth2/auth/requests/consent/{challenge}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/oauth2/auth/requests/consent/{challenge}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Accept an consent request + +``` +PUT /oauth2/auth/requests/consent/{challenge}/accept HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider +to authenticate the user and then tell ORY Hydra now about it. If the user authenticated, he/she must now be asked if +the OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the user's behalf. + +The consent provider which handles this request and is a web app implemented and hosted by you. It shows a user interface which asks the user to +grant or deny the client access to the requested scope ("Application my-dropbox-app wants write access to all your private files"). + +The consent challenge is appended to the consent provider's URL to which the user's user-agent (browser) is redirected to. The consent +provider uses that challenge to fetch information on the OAuth2 request and then tells ORY Hydra if the user accepted +or rejected the request. + +This endpoint tells ORY Hydra that the user has authorized the OAuth 2.0 client to access resources on his/her behalf. +The consent provider includes additional information, such as session data for access and ID tokens, and if the +consent request should be used as basis for future requests. + +The response contains a redirect URL which the consent provider should redirect the user-agent to. + +#### Request body + +```json +{ + "grant_access_token_audience": [ + "string" + ], + "grant_scope": [ + "string" + ], + "remember": true, + "remember_for": 0, + "session": { + "access_token": { + "property1": {}, + "property2": {} + }, + "id_token": { + "property1": {}, + "property2": {} + } + } +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|challenge|path|string|true|none| +|body|body|[acceptConsentRequest](#schemaacceptconsentrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|completedRequest|[completedRequest](#schemacompletedrequest)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "redirect_to": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /oauth2/auth/requests/consent/{challenge}/accept \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/oauth2/auth/requests/consent/{challenge}/accept", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "grant_access_token_audience": [ + "string" + ], + "grant_scope": [ + "string" + ], + "remember": true, + "remember_for": 0, + "session": { + "access_token": { + "property1": {}, + "property2": {} + }, + "id_token": { + "property1": {}, + "property2": {} + } + } +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/oauth2/auth/requests/consent/{challenge}/accept', { + method: 'PUT', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/requests/consent/{challenge}/accept"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.put( + '/oauth2/auth/requests/consent/{challenge}/accept', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.put '/oauth2/auth/requests/consent/{challenge}/accept', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Reject an consent request + +``` +PUT /oauth2/auth/requests/consent/{challenge}/reject HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider +to authenticate the user and then tell ORY Hydra now about it. If the user authenticated, he/she must now be asked if +the OAuth 2.0 Client which initiated the flow should be allowed to access the resources on the user's behalf. + +The consent provider which handles this request and is a web app implemented and hosted by you. It shows a user interface which asks the user to +grant or deny the client access to the requested scope ("Application my-dropbox-app wants write access to all your private files"). + +The consent challenge is appended to the consent provider's URL to which the user's user-agent (browser) is redirected to. The consent +provider uses that challenge to fetch information on the OAuth2 request and then tells ORY Hydra if the user accepted +or rejected the request. + +This endpoint tells ORY Hydra that the user has not authorized the OAuth 2.0 client to access resources on his/her behalf. +The consent provider must include a reason why the consent was not granted. + +The response contains a redirect URL which the consent provider should redirect the user-agent to. + +#### Request body + +```json +{ + "error": "string", + "error_debug": "string", + "error_description": "string", + "error_hint": "string", + "status_code": 0 +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|challenge|path|string|true|none| +|body|body|[rejectRequest](#schemarejectrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|completedRequest|[completedRequest](#schemacompletedrequest)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "redirect_to": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /oauth2/auth/requests/consent/{challenge}/reject \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/oauth2/auth/requests/consent/{challenge}/reject", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "error": "string", + "error_debug": "string", + "error_description": "string", + "error_hint": "string", + "status_code": 0 +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/oauth2/auth/requests/consent/{challenge}/reject', { + method: 'PUT', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/requests/consent/{challenge}/reject"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.put( + '/oauth2/auth/requests/consent/{challenge}/reject', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.put '/oauth2/auth/requests/consent/{challenge}/reject', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Get an login request + +``` +GET /oauth2/auth/requests/login/{challenge} HTTP/1.1 +Accept: application/json + +``` + +When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider +(sometimes called "identity provider") to authenticate the user and then tell ORY Hydra now about it. The login +provider is an web-app you write and host, and it must be able to authenticate ("show the user a login screen") +a user (in OAuth2 the proper name for user is "resource owner"). + +The authentication challenge is appended to the login provider URL to which the user's user-agent (browser) is redirected to. The login +provider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|challenge|path|string|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|loginRequest|[loginRequest](#schemaloginrequest)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "challenge": "string", + "client": { + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" + }, + "oidc_context": { + "acr_values": [ + "string" + ], + "display": "string", + "id_token_hint_claims": { + "property1": {}, + "property2": {} + }, + "login_hint": "string", + "ui_locales": [ + "string" + ] + }, + "request_url": "string", + "requested_access_token_audience": [ + "string" + ], + "requested_scope": [ + "string" + ], + "session_id": "string", + "skip": true, + "subject": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /oauth2/auth/requests/login/{challenge} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/oauth2/auth/requests/login/{challenge}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/oauth2/auth/requests/login/{challenge}', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/requests/login/{challenge}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/oauth2/auth/requests/login/{challenge}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/oauth2/auth/requests/login/{challenge}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Accept an login request + +``` +PUT /oauth2/auth/requests/login/{challenge}/accept HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider +(sometimes called "identity provider") to authenticate the user and then tell ORY Hydra now about it. The login +provider is an web-app you write and host, and it must be able to authenticate ("show the user a login screen") +a user (in OAuth2 the proper name for user is "resource owner"). + +The authentication challenge is appended to the login provider URL to which the user's user-agent (browser) is redirected to. The login +provider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process. + +This endpoint tells ORY Hydra that the user has successfully authenticated and includes additional information such as +the user's ID and if ORY Hydra should remember the user's user agent for future authentication attempts by setting +a cookie. + +The response contains a redirect URL which the login provider should redirect the user-agent to. + +#### Request body + +```json +{ + "acr": "string", + "force_subject_identifier": "string", + "remember": true, + "remember_for": 0, + "subject": "string" +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|challenge|path|string|true|none| +|body|body|[acceptLoginRequest](#schemaacceptloginrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|completedRequest|[completedRequest](#schemacompletedrequest)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "redirect_to": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /oauth2/auth/requests/login/{challenge}/accept \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/oauth2/auth/requests/login/{challenge}/accept", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "acr": "string", + "force_subject_identifier": "string", + "remember": true, + "remember_for": 0, + "subject": "string" +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/oauth2/auth/requests/login/{challenge}/accept', { + method: 'PUT', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/requests/login/{challenge}/accept"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.put( + '/oauth2/auth/requests/login/{challenge}/accept', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.put '/oauth2/auth/requests/login/{challenge}/accept', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Reject a login request + +``` +PUT /oauth2/auth/requests/login/{challenge}/reject HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +When an authorization code, hybrid, or implicit OAuth 2.0 Flow is initiated, ORY Hydra asks the login provider +(sometimes called "identity provider") to authenticate the user and then tell ORY Hydra now about it. The login +provider is an web-app you write and host, and it must be able to authenticate ("show the user a login screen") +a user (in OAuth2 the proper name for user is "resource owner"). + +The authentication challenge is appended to the login provider URL to which the user's user-agent (browser) is redirected to. The login +provider uses that challenge to fetch information on the OAuth2 request and then accept or reject the requested authentication process. + +This endpoint tells ORY Hydra that the user has not authenticated and includes a reason why the authentication +was be denied. + +The response contains a redirect URL which the login provider should redirect the user-agent to. + +#### Request body + +```json +{ + "error": "string", + "error_debug": "string", + "error_description": "string", + "error_hint": "string", + "status_code": 0 +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|challenge|path|string|true|none| +|body|body|[rejectRequest](#schemarejectrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|completedRequest|[completedRequest](#schemacompletedrequest)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "redirect_to": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /oauth2/auth/requests/login/{challenge}/reject \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/oauth2/auth/requests/login/{challenge}/reject", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "error": "string", + "error_debug": "string", + "error_description": "string", + "error_hint": "string", + "status_code": 0 +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/oauth2/auth/requests/login/{challenge}/reject', { + method: 'PUT', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/requests/login/{challenge}/reject"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.put( + '/oauth2/auth/requests/login/{challenge}/reject', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.put '/oauth2/auth/requests/login/{challenge}/reject', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Lists all consent sessions of a user + +``` +GET /oauth2/auth/sessions/consent/{user} HTTP/1.1 +Accept: application/json + +``` + +This endpoint lists all user's granted consent sessions, including client and granted scope + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|user|path|string|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A list of handled consent requests.|Inline| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + + +##### Response Schema + +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[[PreviousConsentSession](#schemapreviousconsentsession)]|false|none|[The response used to return handled consent requests same as HandledAuthenticationRequest, just with consent_request exposed as json]| +|» consent_request|[consentRequest](#schemaconsentrequest)|false|none|none| +|»» acr|string|false|none|ACR represents the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication.| +|»» challenge|string|false|none|Challenge is the identifier ("authorization challenge") of the consent authorization request. It is used to identify the session.| +|»» client|[oAuth2Client](#schemaoauth2client)|false|none|none| +|»»» allowed_cors_origins|[string]|false|none|AllowedCORSOrigins are one or more URLs (scheme://host[:port]) which are allowed to make CORS requests to the /oauth/token endpoint. If this array is empty, the sever's CORS origin configuration (`CORS_ALLOWED_ORIGINS`) will be used instead. If this array is set, the allowed origins are appended to the server's CORS origin configuration. Be aware that environment variable `CORS_ENABLED` MUST be set to `true` for this to work.| +|»»» audience|[string]|false|none|Audience is a whitelist defining the audiences this client is allowed to request tokens for. An audience limits the applicability of an OAuth 2.0 Access Token to, for example, certain API endpoints. The value is a list of URLs. URLs MUST NOT contain whitespaces.| +|»»» client_id|string|false|none|ClientID is the id for this client.| +|»»» client_name|string|false|none|Name is the human-readable string name of the client to be presented to the end-user during authorization.| +|»»» client_secret|string|false|none|Secret is the client's secret. The secret will be included in the create request as cleartext, and then never again. The secret is stored using BCrypt so it is impossible to recover it. Tell your users that they need to write the secret down as it will not be made available again.| +|»»» client_secret_expires_at|integer(int64)|false|none|SecretExpiresAt is an integer holding the time at which the client secret will expire or 0 if it will not expire. The time is represented as the number of seconds from 1970-01-01T00:00:00Z as measured in UTC until the date/time of expiration.| +|»»» client_uri|string|false|none|ClientURI is an URL string of a web page providing information about the client. If present, the server SHOULD display this URL to the end-user in a clickable fashion.| +|»»» contacts|[string]|false|none|Contacts is a array of strings representing ways to contact people responsible for this client, typically email addresses.| +|»»» grant_types|[string]|false|none|GrantTypes is an array of grant types the client is allowed to use.| +|»»» jwks|[JSONWebKeySet](#schemajsonwebkeyset)|false|none|none| +|»»»» keys|[[JSONWebKey](#schemajsonwebkey)]|false|none|The value of the "keys" parameter is an array of JWK values. By default, the order of the JWK values within the array does not imply an order of preference among them, although applications of JWK Sets can choose to assign a meaning to the order for their purposes, if desired.| +|»»»»» alg|string|true|none|The "alg" (algorithm) parameter identifies the algorithm intended for use with the key. The values used should either be registered in the IANA "JSON Web Signature and Encryption Algorithms" registry established by [JWA] or be a value that contains a Collision- Resistant Name.| +|»»»»» crv|string|false|none|none| +|»»»»» d|string|false|none|none| +|»»»»» dp|string|false|none|none| +|»»»»» dq|string|false|none|none| +|»»»»» e|string|false|none|none| +|»»»»» k|string|false|none|none| +|»»»»» kid|string|true|none|The "kid" (key ID) parameter is used to match a specific key. This is used, for instance, to choose among a set of keys within a JWK Set during key rollover. The structure of the "kid" value is unspecified. When "kid" values are used within a JWK Set, different keys within the JWK Set SHOULD use distinct "kid" values. (One example in which different keys might use the same "kid" value is if they have different "kty" (key type) values but are considered to be equivalent alternatives by the application using them.) The "kid" value is a case-sensitive string.| +|»»»»» kty|string|true|none|The "kty" (key type) parameter identifies the cryptographic algorithm family used with the key, such as "RSA" or "EC". "kty" values should either be registered in the IANA "JSON Web Key Types" registry established by [JWA] or be a value that contains a Collision- Resistant Name. The "kty" value is a case-sensitive string.| +|»»»»» n|string|false|none|none| +|»»»»» p|string|false|none|none| +|»»»»» q|string|false|none|none| +|»»»»» qi|string|false|none|none| +|»»»»» use|string|true|none|Use ("public key use") identifies the intended use of the public key. The "use" parameter is employed to indicate whether a public key is used for encrypting data or verifying the signature on data. Values are commonly "sig" (signature) or "enc" (encryption).| +|»»»»» x|string|false|none|none| +|»»»»» x5c|[string]|false|none|The "x5c" (X.509 certificate chain) parameter contains a chain of one or more PKIX certificates [RFC5280]. The certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (Section 4 of [RFC4648] -- not base64url-encoded) DER [ITU.X690.1994] PKIX certificate value. The PKIX certificate containing the key value MUST be the first certificate.| +|»»»»» y|string|false|none|none| +|»»»» jwks_uri|string|false|none|URL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains the signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the Client's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing and encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both signatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.| +|»»»» logo_uri|string|false|none|LogoURI is an URL string that references a logo for the client.| +|»»»» owner|string|false|none|Owner is a string identifying the owner of the OAuth 2.0 Client.| +|»»»» policy_uri|string|false|none|PolicyURI is a URL string that points to a human-readable privacy policy document that describes how the deployment organization collects, uses, retains, and discloses personal data.| +|»»»» redirect_uris|[string]|false|none|RedirectURIs is an array of allowed redirect urls for the client, for example http://mydomain/oauth/callback .| +|»»»» request_object_signing_alg|string|false|none|JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm.| +|»»»» request_uris|[string]|false|none|Array of request_uri values that are pre-registered by the RP for use at the OP. Servers MAY cache the contents of the files referenced by these URIs and not retrieve them at the time they are used in a request. OPs can require that request_uri values used be pre-registered with the require_request_uri_registration discovery parameter.| +|»»»» response_types|[string]|false|none|ResponseTypes is an array of the OAuth 2.0 response type strings that the client can use at the authorization endpoint.| +|»»»» scope|string|false|none|Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens.| +|»»»» sector_identifier_uri|string|false|none|URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values.| +|»»»» subject_type|string|false|none|SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`.| +|»»»» token_endpoint_auth_method|string|false|none|Requested Client Authentication method for the Token Endpoint. The options are client_secret_post, client_secret_basic, private_key_jwt, and none.| +|»»»» tos_uri|string|false|none|TermsOfServiceURI is a URL string that points to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client.| +|»»»» userinfo_signed_response_alg|string|false|none|JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims as a UTF-8 encoded JSON object using the application/json content-type.| +|»»» login_challenge|string|false|none|LoginChallenge is the login challenge this consent challenge belongs to. It can be used to associate a login and consent request in the login & consent app.| +|»»» login_session_id|string|false|none|LoginSessionID is the authentication session ID. It is set if the browser had a valid authentication session at ORY Hydra during the login flow. It can be used to associate consecutive login requests by a certain user.| +|»»» oidc_context|[openIDConnectContext](#schemaopenidconnectcontext)|false|none|none| +|»»»» acr_values|[string]|false|none|ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request. It is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required. OpenID Connect defines it as follows: > Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a Voluntary Claim by this parameter.| +|»»»» display|string|false|none|Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User. The defined values are: page: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode. popup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over. touch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface. wap: The Authorization Server SHOULD display the authentication and consent UI consistent with a "feature phone" type display. The Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display.| +|»»»» id_token_hint_claims|object|false|none|IDTokenHintClaims are the claims of the ID Token previously issued by the Authorization Server being passed as a hint about the End-User's current or past authenticated session with the Client.| +|»»»»» **additionalProperties**|object|false|none|none| +|»»»» login_hint|string|false|none|LoginHint hints about the login identifier the End-User might use to log in (if necessary). This hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier) and then wants to pass that value as a hint to the discovered authorization service. This value MAY also be a phone number in the format specified for the phone_number Claim. The use of this parameter is optional.| +|»»»» ui_locales|[string]|false|none|UILocales is the End-User'id preferred languages and scripts for the user interface, represented as a space-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value "fr-CA fr en" represents a preference for French as spoken in Canada, then French (without a region designation), followed by English (without a region designation). An error SHOULD NOT result if some or all of the requested locales are not supported by the OpenID Provider.| +|»»» request_url|string|false|none|RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters.| +|»»» requested_access_token_audience|[string]|false|none|RequestedScope contains the access token audience as requested by the OAuth 2.0 Client.| +|»»» requested_scope|[string]|false|none|RequestedScope contains the OAuth 2.0 Scope requested by the OAuth 2.0 Client.| +|»»» skip|boolean|false|none|Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you must not ask the user to grant the requested scopes. You must however either allow or deny the consent request using the usual API call.| +|»»» subject|string|false|none|Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client.| +|»» grant_access_token_audience|[string]|false|none|GrantedAudience sets the audience the user authorized the client to use. Should be a subset of `requested_access_token_audience`.| +|»» grant_scope|[string]|false|none|GrantScope sets the scope the user authorized the client to use. Should be a subset of `requested_scope`| +|»» remember|boolean|false|none|Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same client asks the same user for the same, or a subset of, scope.| +|»» remember_for|integer(int64)|false|none|RememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the authorization will be remembered indefinitely.| +|»» session|[consentRequestSession](#schemaconsentrequestsession)|false|none|none| +|»»» access_token|object|false|none|AccessToken sets session data for the access and refresh token, as well as any future tokens issued by the refresh grant. Keep in mind that this data will be available to anyone performing OAuth 2.0 Challenge Introspection. If only your services can perform OAuth 2.0 Challenge Introspection, this is usually fine. But if third parties can access that endpoint as well, sensitive data from the session might be exposed to them. Use with care!| +|»»»» **additionalProperties**|object|false|none|none| +|»»» id_token|object|false|none|IDToken sets session data for the OpenID Connect ID token. Keep in mind that the session'id payloads are readable by anyone that has access to the ID Challenge. Use with care!| +|»»»» **additionalProperties**|object|false|none|none| + +##### Examples + +###### 200 response + +```json +[ + { + "consent_request": { + "acr": "string", + "challenge": "string", + "client": { + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" + }, + "login_challenge": "string", + "login_session_id": "string", + "oidc_context": { + "acr_values": [ + "string" + ], + "display": "string", + "id_token_hint_claims": { + "property1": {}, + "property2": {} + }, + "login_hint": "string", + "ui_locales": [ + "string" + ] + }, + "request_url": "string", + "requested_access_token_audience": [ + "string" + ], + "requested_scope": [ + "string" + ], + "skip": true, + "subject": "string" + }, + "grant_access_token_audience": [ + "string" + ], + "grant_scope": [ + "string" + ], + "remember": true, + "remember_for": 0, + "session": { + "access_token": { + "property1": {}, + "property2": {} + }, + "id_token": { + "property1": {}, + "property2": {} + } + } + } +] +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /oauth2/auth/sessions/consent/{user} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/oauth2/auth/sessions/consent/{user}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/oauth2/auth/sessions/consent/{user}', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/sessions/consent/{user}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/oauth2/auth/sessions/consent/{user}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/oauth2/auth/sessions/consent/{user}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Revokes all previous consent sessions of a user + +``` +DELETE /oauth2/auth/sessions/consent/{user} HTTP/1.1 +Accept: application/json + +``` + +This endpoint revokes a user's granted consent sessions and invalidates all associated OAuth 2.0 Access Tokens. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|user|path|string|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.|None| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 404 response + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /oauth2/auth/sessions/consent/{user} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/oauth2/auth/sessions/consent/{user}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/oauth2/auth/sessions/consent/{user}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/sessions/consent/{user}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/oauth2/auth/sessions/consent/{user}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/oauth2/auth/sessions/consent/{user}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Revokes consent sessions of a user for a specific OAuth 2.0 Client + +``` +DELETE /oauth2/auth/sessions/consent/{user}/{client} HTTP/1.1 +Accept: application/json + +``` + +This endpoint revokes a user's granted consent sessions for a specific OAuth 2.0 Client and invalidates all +associated OAuth 2.0 Access Tokens. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|user|path|string|true|none| +|client|path|string|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.|None| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 404 response + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /oauth2/auth/sessions/consent/{user}/{client} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/oauth2/auth/sessions/consent/{user}/{client}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/oauth2/auth/sessions/consent/{user}/{client}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/sessions/consent/{user}/{client}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/oauth2/auth/sessions/consent/{user}/{client}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/oauth2/auth/sessions/consent/{user}/{client}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Logs user out by deleting the session cookie + +``` +GET /oauth2/auth/sessions/login/revoke HTTP/1.1 +Accept: application/json + +``` + +This endpoint deletes ths user's login session cookie and redirects the browser to the url +listed in `LOGOUT_REDIRECT_URL` environment variable. This endpoint does not work as an API but has to +be called from the user's browser. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|302|[Found](https://tools.ietf.org/html/rfc7231#section-6.4.3)|Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.|None| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 404 response + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /oauth2/auth/sessions/login/revoke \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/oauth2/auth/sessions/login/revoke", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/oauth2/auth/sessions/login/revoke', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/sessions/login/revoke"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/oauth2/auth/sessions/login/revoke', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/oauth2/auth/sessions/login/revoke', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Invalidates a user's authentication session + +``` +DELETE /oauth2/auth/sessions/login/{user} HTTP/1.1 +Accept: application/json + +``` + +This endpoint invalidates a user's authentication session. After revoking the authentication session, the user +has to re-authenticate at ORY Hydra. This endpoint does not invalidate any tokens. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|user|path|string|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.|None| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 404 response + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /oauth2/auth/sessions/login/{user} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/oauth2/auth/sessions/login/{user}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/oauth2/auth/sessions/login/{user}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/auth/sessions/login/{user}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/oauth2/auth/sessions/login/{user}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/oauth2/auth/sessions/login/{user}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Flush Expired OAuth2 Access Tokens + +``` +POST /oauth2/flush HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +This endpoint flushes expired OAuth2 access tokens from the database. You can set a time after which no tokens will be +not be touched, in case you want to keep recent tokens for auditing. Refresh tokens can not be flushed as they are deleted +automatically when performing the refresh flow. + +#### Request body + +```json +{ + "notAfter": "2018-11-15T08:23:46Z" +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[flushInactiveOAuth2TokensRequest](#schemaflushinactiveoauth2tokensrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 401 response + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /oauth2/flush \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/oauth2/flush", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "notAfter": "2018-11-15T08:23:46Z" +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/oauth2/flush', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/flush"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/oauth2/flush', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/oauth2/flush', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Introspect OAuth2 tokens + +``` +POST /oauth2/introspect HTTP/1.1 +Content-Type: application/x-www-form-urlencoded +Accept: application/json + +``` + +The introspection endpoint allows to check if a token (both refresh and access) is active or not. An active token +is neither expired nor revoked. If a token is active, additional information on the token will be included. You can +set additional data for a token by setting `accessTokenExtra` during the consent flow. + +#### Request body + +```yaml +token: string +scope: string + +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|object|false|none| +|» token|body|string|true|The string value of the token. For access tokens, this| +|» scope|body|string|false|An optional, space separated list of required scopes. If the access token was not granted one of the| + +##### Detailed descriptions + +**» token**: The string value of the token. For access tokens, this +is the "access_token" value returned from the token endpoint +defined in OAuth 2.0 [RFC6749], Section 5.1. +This endpoint DOES NOT accept refresh tokens for validation. + +**» scope**: An optional, space separated list of required scopes. If the access token was not granted one of the +scopes, the result of active will be false. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|oAuth2TokenIntrospection|[oAuth2TokenIntrospection](#schemaoauth2tokenintrospection)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|genericError|[genericError](#schemagenericerror)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "active": true, + "aud": [ + "string" + ], + "client_id": "string", + "exp": 0, + "ext": { + "property1": {}, + "property2": {} + }, + "iat": 0, + "iss": "string", + "nbf": 0, + "obfuscated_subject": "string", + "scope": "string", + "sub": "string", + "token_type": "string", + "username": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /oauth2/introspect \ + -H 'Content-Type: application/x-www-form-urlencoded' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/x-www-form-urlencoded"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/oauth2/introspect", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "token": "string", + "scope": "string" +}'; +const headers = { + 'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'application/json' +} + +fetch('/oauth2/introspect', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/oauth2/introspect"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json' +} + +r = requests.post( + '/oauth2/introspect', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/x-www-form-urlencoded', + 'Accept' => 'application/json' +} + +result = RestClient.post '/oauth2/introspect', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## health + + + +### Check alive status + +``` +GET /health/alive HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns a 200 status code when the HTTP server is up running. +This status does currently not include checks whether the database connection is working. + +If the service supports TLS Edge Termination, this endpoint does not require the +`X-Forwarded-Proto` header to be set. + +Be aware that if you are running multiple nodes of this service, the health status will never +refer to the cluster state, only to a single instance. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|healthStatus|[healthStatus](#schemahealthstatus)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|genericError|[genericError](#schemagenericerror)| + +##### Examples + +###### 200 response + +```json +{ + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /health/alive \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/health/alive", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/health/alive', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/health/alive"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/health/alive', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/health/alive', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Check readiness status + +``` +GET /health/ready HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g. +the database) are responsive as well. + +If the service supports TLS Edge Termination, this endpoint does not require the +`X-Forwarded-Proto` header to be set. + +Be aware that if you are running multiple nodes of this service, the health status will never +refer to the cluster state, only to a single instance. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|healthStatus|[healthStatus](#schemahealthstatus)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|healthNotReadyStatus|[healthNotReadyStatus](#schemahealthnotreadystatus)| + +##### Examples + +###### 200 response + +```json +{ + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /health/ready \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/health/ready", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/health/ready', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/health/ready"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/health/ready', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/health/ready', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## version + + + +### Get service version + +``` +GET /version HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns the service version typically notated using semantic versioning. + +If the service supports TLS Edge Termination, this endpoint does not require the +`X-Forwarded-Proto` header to be set. + +Be aware that if you are running multiple nodes of this service, the health status will never +refer to the cluster state, only to a single instance. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|version|[version](#schemaversion)| + +##### Examples + +###### 200 response + +```json +{ + "version": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /version \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/version", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/version', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/version"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/version', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/version', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ +## Schemas + +AuthenticationSession +#### AuthenticationSession + + + +```json +{ + "AuthenticatedAt": "2018-11-15T08:23:46Z", + "ID": "string", + "Subject": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|AuthenticatedAt|string(date-time)|false|none|none| +|ID|string|false|none|none| +|Subject|string|false|none|none| + +JSONWebKey +#### JSONWebKey + + + +```json +{ + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|alg|string|true|none|The "alg" (algorithm) parameter identifies the algorithm intended for use with the key. The values used should either be registered in the IANA "JSON Web Signature and Encryption Algorithms" registry established by [JWA] or be a value that contains a Collision- Resistant Name.| +|crv|string|false|none|none| +|d|string|false|none|none| +|dp|string|false|none|none| +|dq|string|false|none|none| +|e|string|false|none|none| +|k|string|false|none|none| +|kid|string|true|none|The "kid" (key ID) parameter is used to match a specific key. This is used, for instance, to choose among a set of keys within a JWK Set during key rollover. The structure of the "kid" value is unspecified. When "kid" values are used within a JWK Set, different keys within the JWK Set SHOULD use distinct "kid" values. (One example in which different keys might use the same "kid" value is if they have different "kty" (key type) values but are considered to be equivalent alternatives by the application using them.) The "kid" value is a case-sensitive string.| +|kty|string|true|none|The "kty" (key type) parameter identifies the cryptographic algorithm family used with the key, such as "RSA" or "EC". "kty" values should either be registered in the IANA "JSON Web Key Types" registry established by [JWA] or be a value that contains a Collision- Resistant Name. The "kty" value is a case-sensitive string.| +|n|string|false|none|none| +|p|string|false|none|none| +|q|string|false|none|none| +|qi|string|false|none|none| +|use|string|true|none|Use ("public key use") identifies the intended use of the public key. The "use" parameter is employed to indicate whether a public key is used for encrypting data or verifying the signature on data. Values are commonly "sig" (signature) or "enc" (encryption).| +|x|string|false|none|none| +|x5c|[string]|false|none|The "x5c" (X.509 certificate chain) parameter contains a chain of one or more PKIX certificates [RFC5280]. The certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (Section 4 of [RFC4648] -- not base64url-encoded) DER [ITU.X690.1994] PKIX certificate value. The PKIX certificate containing the key value MUST be the first certificate.| +|y|string|false|none|none| + +JSONWebKeySet +#### JSONWebKeySet + + + +```json +{ + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|keys|[[JSONWebKey](#schemajsonwebkey)]|false|none|The value of the "keys" parameter is an array of JWK values. By default, the order of the JWK values within the array does not imply an order of preference among them, although applications of JWK Sets can choose to assign a meaning to the order for their purposes, if desired.| + +PreviousConsentSession +#### PreviousConsentSession + + + +```json +{ + "consent_request": { + "acr": "string", + "challenge": "string", + "client": { + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" + }, + "login_challenge": "string", + "login_session_id": "string", + "oidc_context": { + "acr_values": [ + "string" + ], + "display": "string", + "id_token_hint_claims": { + "property1": {}, + "property2": {} + }, + "login_hint": "string", + "ui_locales": [ + "string" + ] + }, + "request_url": "string", + "requested_access_token_audience": [ + "string" + ], + "requested_scope": [ + "string" + ], + "skip": true, + "subject": "string" + }, + "grant_access_token_audience": [ + "string" + ], + "grant_scope": [ + "string" + ], + "remember": true, + "remember_for": 0, + "session": { + "access_token": { + "property1": {}, + "property2": {} + }, + "id_token": { + "property1": {}, + "property2": {} + } + } +} + +``` + +*The response used to return handled consent requests +same as HandledAuthenticationRequest, just with consent_request exposed as json* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|consent_request|[consentRequest](#schemaconsentrequest)|false|none|none| +|grant_access_token_audience|[string]|false|none|GrantedAudience sets the audience the user authorized the client to use. Should be a subset of `requested_access_token_audience`.| +|grant_scope|[string]|false|none|GrantScope sets the scope the user authorized the client to use. Should be a subset of `requested_scope`| +|remember|boolean|false|none|Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same client asks the same user for the same, or a subset of, scope.| +|remember_for|integer(int64)|false|none|RememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the authorization will be remembered indefinitely.| +|session|[consentRequestSession](#schemaconsentrequestsession)|false|none|none| + +acceptConsentRequest +#### acceptConsentRequest + + + +```json +{ + "grant_access_token_audience": [ + "string" + ], + "grant_scope": [ + "string" + ], + "remember": true, + "remember_for": 0, + "session": { + "access_token": { + "property1": {}, + "property2": {} + }, + "id_token": { + "property1": {}, + "property2": {} + } + } +} + +``` + +*The request payload used to accept a consent request.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|grant_access_token_audience|[string]|false|none|GrantedAudience sets the audience the user authorized the client to use. Should be a subset of `requested_access_token_audience`.| +|grant_scope|[string]|false|none|GrantScope sets the scope the user authorized the client to use. Should be a subset of `requested_scope`.| +|remember|boolean|false|none|Remember, if set to true, tells ORY Hydra to remember this consent authorization and reuse it if the same client asks the same user for the same, or a subset of, scope.| +|remember_for|integer(int64)|false|none|RememberFor sets how long the consent authorization should be remembered for in seconds. If set to `0`, the authorization will be remembered indefinitely.| +|session|[consentRequestSession](#schemaconsentrequestsession)|false|none|none| + +acceptLoginRequest +#### acceptLoginRequest + + + +```json +{ + "acr": "string", + "force_subject_identifier": "string", + "remember": true, + "remember_for": 0, + "subject": "string" +} + +``` + +*The request payload used to accept a login request.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|acr|string|false|none|ACR sets the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication.| +|force_subject_identifier|string|false|none|ForceSubjectIdentifier forces the "pairwise" user ID of the end-user that authenticated. The "pairwise" user ID refers to the (Pairwise Identifier Algorithm)[http://openid.net/specs/openid-connect-core-1_0.html#PairwiseAlg] of the OpenID Connect specification. It allows you to set an obfuscated subject ("user") identifier that is unique to the client. Please note that this changes the user ID on endpoint /userinfo and sub claim of the ID Token. It does not change the sub claim in the OAuth 2.0 Introspection. Per default, ORY Hydra handles this value with its own algorithm. In case you want to set this yourself you can use this field. Please note that setting this field has no effect if `pairwise` is not configured in ORY Hydra or the OAuth 2.0 Client does not expect a pairwise identifier (set via `subject_type` key in the client's configuration). Please also be aware that ORY Hydra is unable to properly compute this value during authentication. This implies that you have to compute this value on every authentication process (probably depending on the client ID or some other unique value). If you fail to compute the proper value, then authentication processes which have id_token_hint set might fail.| +|remember|boolean|false|none|Remember, if set to true, tells ORY Hydra to remember this user by telling the user agent (browser) to store a cookie with authentication data. If the same user performs another OAuth 2.0 Authorization Request, he/she will not be asked to log in again.| +|remember_for|integer(int64)|false|none|RememberFor sets how long the authentication should be remembered for in seconds. If set to `0`, the authorization will be remembered indefinitely.| +|subject|string|false|none|Subject is the user ID of the end-user that authenticated.| + +completedRequest +#### completedRequest + + + +```json +{ + "redirect_to": "string" +} + +``` + +*The response payload sent when accepting or rejecting a login or consent request.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|redirect_to|string|false|none|RedirectURL is the URL which you should redirect the user to once the authentication process is completed.| + +consentRequest +#### consentRequest + + + +```json +{ + "acr": "string", + "challenge": "string", + "client": { + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" + }, + "login_challenge": "string", + "login_session_id": "string", + "oidc_context": { + "acr_values": [ + "string" + ], + "display": "string", + "id_token_hint_claims": { + "property1": {}, + "property2": {} + }, + "login_hint": "string", + "ui_locales": [ + "string" + ] + }, + "request_url": "string", + "requested_access_token_audience": [ + "string" + ], + "requested_scope": [ + "string" + ], + "skip": true, + "subject": "string" +} + +``` + +*Contains information on an ongoing consent request.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|acr|string|false|none|ACR represents the Authentication AuthorizationContext Class Reference value for this authentication session. You can use it to express that, for example, a user authenticated using two factor authentication.| +|challenge|string|false|none|Challenge is the identifier ("authorization challenge") of the consent authorization request. It is used to identify the session.| +|client|[oAuth2Client](#schemaoauth2client)|false|none|none| +|login_challenge|string|false|none|LoginChallenge is the login challenge this consent challenge belongs to. It can be used to associate a login and consent request in the login & consent app.| +|login_session_id|string|false|none|LoginSessionID is the authentication session ID. It is set if the browser had a valid authentication session at ORY Hydra during the login flow. It can be used to associate consecutive login requests by a certain user.| +|oidc_context|[openIDConnectContext](#schemaopenidconnectcontext)|false|none|none| +|request_url|string|false|none|RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters.| +|requested_access_token_audience|[string]|false|none|RequestedScope contains the access token audience as requested by the OAuth 2.0 Client.| +|requested_scope|[string]|false|none|RequestedScope contains the OAuth 2.0 Scope requested by the OAuth 2.0 Client.| +|skip|boolean|false|none|Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you must not ask the user to grant the requested scopes. You must however either allow or deny the consent request using the usual API call.| +|subject|string|false|none|Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client.| + +consentRequestSession +#### consentRequestSession + + + +```json +{ + "access_token": { + "property1": {}, + "property2": {} + }, + "id_token": { + "property1": {}, + "property2": {} + } +} + +``` + +*Used to pass session data to a consent request.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|access_token|object|false|none|AccessToken sets session data for the access and refresh token, as well as any future tokens issued by the refresh grant. Keep in mind that this data will be available to anyone performing OAuth 2.0 Challenge Introspection. If only your services can perform OAuth 2.0 Challenge Introspection, this is usually fine. But if third parties can access that endpoint as well, sensitive data from the session might be exposed to them. Use with care!| +|» **additionalProperties**|object|false|none|none| +|id_token|object|false|none|IDToken sets session data for the OpenID Connect ID token. Keep in mind that the session'id payloads are readable by anyone that has access to the ID Challenge. Use with care!| +|» **additionalProperties**|object|false|none|none| + +emptyResponse +#### emptyResponse + + + +```json +{} + +``` + +*Empty responses are sent when, for example, resources are deleted. The HTTP status code for empty responses is +typically 201.* + +#### Properties + +*None* + +flushInactiveOAuth2TokensRequest +#### flushInactiveOAuth2TokensRequest + + + +```json +{ + "notAfter": "2018-11-15T08:23:46Z" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|notAfter|string(date-time)|false|none|NotAfter sets after which point tokens should not be flushed. This is useful when you want to keep a history of recently issued tokens for auditing.| + +genericError +#### genericError + + + +```json +{ + "error": "The requested resource could not be found", + "error_code": 404, + "error_debug": "The database adapter was unable to find the element", + "error_hint": "Object with ID 12345 does not exist" +} + +``` + +*Error response* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|error|string|true|none|Name is the error name.| +|error_code|integer(int64)|false|none|Code represents the error status code (404, 403, 401, ...).| +|error_debug|string|false|none|Debug contains debug information. This is usually not available and has to be enabled.| +|error_hint|string|false|none|Hint contains further information on the nature of the error.| + +healthNotReadyStatus +#### healthNotReadyStatus + + + +```json +{ + "errors": { + "property1": "string", + "property2": "string" + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|errors|object|false|none|Errors contains a list of errors that caused the not ready status.| +|» **additionalProperties**|string|false|none|none| + +healthStatus +#### healthStatus + + + +```json +{ + "status": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|Status always contains "ok".| + +jsonWebKeySetGeneratorRequest +#### jsonWebKeySetGeneratorRequest + + + +```json +{ + "alg": "string", + "kid": "string", + "use": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|alg|string|true|none|The algorithm to be used for creating the key. Supports "RS256", "ES512", "HS512", and "HS256"| +|kid|string|true|none|The kid of the key to be created| +|use|string|true|none|The "use" (public key use) parameter identifies the intended use of the public key. The "use" parameter is employed to indicate whether a public key is used for encrypting data or verifying the signature on data. Valid values are "enc" and "sig".| + +loginRequest +#### loginRequest + + + +```json +{ + "challenge": "string", + "client": { + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" + }, + "oidc_context": { + "acr_values": [ + "string" + ], + "display": "string", + "id_token_hint_claims": { + "property1": {}, + "property2": {} + }, + "login_hint": "string", + "ui_locales": [ + "string" + ] + }, + "request_url": "string", + "requested_access_token_audience": [ + "string" + ], + "requested_scope": [ + "string" + ], + "session_id": "string", + "skip": true, + "subject": "string" +} + +``` + +*Contains information on an ongoing login request.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|challenge|string|false|none|Challenge is the identifier ("authentication challenge") of the consent authentication request. It is used to identify the session.| +|client|[oAuth2Client](#schemaoauth2client)|false|none|none| +|oidc_context|[openIDConnectContext](#schemaopenidconnectcontext)|false|none|none| +|request_url|string|false|none|RequestURL is the original OAuth 2.0 Authorization URL requested by the OAuth 2.0 client. It is the URL which initiates the OAuth 2.0 Authorization Code or OAuth 2.0 Implicit flow. This URL is typically not needed, but might come in handy if you want to deal with additional request parameters.| +|requested_access_token_audience|[string]|false|none|RequestedScope contains the access token audience as requested by the OAuth 2.0 Client.| +|requested_scope|[string]|false|none|RequestedScope contains the OAuth 2.0 Scope requested by the OAuth 2.0 Client.| +|session_id|string|false|none|SessionID is the authentication session ID. It is set if the browser had a valid authentication session at ORY Hydra during the login flow. It can be used to associate consecutive login requests by a certain user.| +|skip|boolean|false|none|Skip, if true, implies that the client has requested the same scopes from the same user previously. If true, you can skip asking the user to grant the requested scopes, and simply forward the user to the redirect URL. This feature allows you to update / set session information.| +|subject|string|false|none|Subject is the user ID of the end-user that authenticated. Now, that end user needs to grant or deny the scope requested by the OAuth 2.0 client. If this value is set and `skip` is true, you MUST include this subject type when accepting the login request, or the request will fail.| + +oAuth2Client +#### oAuth2Client + + + +```json +{ + "allowed_cors_origins": [ + "string" + ], + "audience": [ + "string" + ], + "client_id": "string", + "client_name": "string", + "client_secret": "string", + "client_secret_expires_at": 0, + "client_uri": "string", + "contacts": [ + "string" + ], + "grant_types": [ + "string" + ], + "jwks": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "jwks_uri": "string", + "logo_uri": "string", + "owner": "string", + "policy_uri": "string", + "redirect_uris": [ + "string" + ], + "request_object_signing_alg": "string", + "request_uris": [ + "string" + ], + "response_types": [ + "string" + ], + "scope": "string", + "sector_identifier_uri": "string", + "subject_type": "string", + "token_endpoint_auth_method": "string", + "tos_uri": "string", + "userinfo_signed_response_alg": "string" +} + +``` + +*Client represents an OAuth 2.0 Client.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed_cors_origins|[string]|false|none|AllowedCORSOrigins are one or more URLs (scheme://host[:port]) which are allowed to make CORS requests to the /oauth/token endpoint. If this array is empty, the sever's CORS origin configuration (`CORS_ALLOWED_ORIGINS`) will be used instead. If this array is set, the allowed origins are appended to the server's CORS origin configuration. Be aware that environment variable `CORS_ENABLED` MUST be set to `true` for this to work.| +|audience|[string]|false|none|Audience is a whitelist defining the audiences this client is allowed to request tokens for. An audience limits the applicability of an OAuth 2.0 Access Token to, for example, certain API endpoints. The value is a list of URLs. URLs MUST NOT contain whitespaces.| +|client_id|string|false|none|ClientID is the id for this client.| +|client_name|string|false|none|Name is the human-readable string name of the client to be presented to the end-user during authorization.| +|client_secret|string|false|none|Secret is the client's secret. The secret will be included in the create request as cleartext, and then never again. The secret is stored using BCrypt so it is impossible to recover it. Tell your users that they need to write the secret down as it will not be made available again.| +|client_secret_expires_at|integer(int64)|false|none|SecretExpiresAt is an integer holding the time at which the client secret will expire or 0 if it will not expire. The time is represented as the number of seconds from 1970-01-01T00:00:00Z as measured in UTC until the date/time of expiration.| +|client_uri|string|false|none|ClientURI is an URL string of a web page providing information about the client. If present, the server SHOULD display this URL to the end-user in a clickable fashion.| +|contacts|[string]|false|none|Contacts is a array of strings representing ways to contact people responsible for this client, typically email addresses.| +|grant_types|[string]|false|none|GrantTypes is an array of grant types the client is allowed to use.| +|jwks|[JSONWebKeySet](#schemajsonwebkeyset)|false|none|none| +|jwks_uri|string|false|none|URL for the Client's JSON Web Key Set [JWK] document. If the Client signs requests to the Server, it contains the signing key(s) the Server uses to validate signatures from the Client. The JWK Set MAY also contain the Client's encryption keys(s), which are used by the Server to encrypt responses to the Client. When both signing and encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both signatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.| +|logo_uri|string|false|none|LogoURI is an URL string that references a logo for the client.| +|owner|string|false|none|Owner is a string identifying the owner of the OAuth 2.0 Client.| +|policy_uri|string|false|none|PolicyURI is a URL string that points to a human-readable privacy policy document that describes how the deployment organization collects, uses, retains, and discloses personal data.| +|redirect_uris|[string]|false|none|RedirectURIs is an array of allowed redirect urls for the client, for example http://mydomain/oauth/callback .| +|request_object_signing_alg|string|false|none|JWS [JWS] alg algorithm [JWA] that MUST be used for signing Request Objects sent to the OP. All Request Objects from this Client MUST be rejected, if not signed with this algorithm.| +|request_uris|[string]|false|none|Array of request_uri values that are pre-registered by the RP for use at the OP. Servers MAY cache the contents of the files referenced by these URIs and not retrieve them at the time they are used in a request. OPs can require that request_uri values used be pre-registered with the require_request_uri_registration discovery parameter.| +|response_types|[string]|false|none|ResponseTypes is an array of the OAuth 2.0 response type strings that the client can use at the authorization endpoint.| +|scope|string|false|none|Scope is a string containing a space-separated list of scope values (as described in Section 3.3 of OAuth 2.0 [RFC6749]) that the client can use when requesting access tokens.| +|sector_identifier_uri|string|false|none|URL using the https scheme to be used in calculating Pseudonymous Identifiers by the OP. The URL references a file with a single JSON array of redirect_uri values.| +|subject_type|string|false|none|SubjectType requested for responses to this Client. The subject_types_supported Discovery parameter contains a list of the supported subject_type values for this server. Valid types include `pairwise` and `public`.| +|token_endpoint_auth_method|string|false|none|Requested Client Authentication method for the Token Endpoint. The options are client_secret_post, client_secret_basic, private_key_jwt, and none.| +|tos_uri|string|false|none|TermsOfServiceURI is a URL string that points to a human-readable terms of service document for the client that describes a contractual relationship between the end-user and the client that the end-user accepts when authorizing the client.| +|userinfo_signed_response_alg|string|false|none|JWS alg algorithm [JWA] REQUIRED for signing UserInfo Responses. If this is specified, the response will be JWT [JWT] serialized, and signed using JWS. The default, if omitted, is for the UserInfo Response to return the Claims as a UTF-8 encoded JSON object using the application/json content-type.| + +oAuth2TokenIntrospection +#### oAuth2TokenIntrospection + + + +```json +{ + "active": true, + "aud": [ + "string" + ], + "client_id": "string", + "exp": 0, + "ext": { + "property1": {}, + "property2": {} + }, + "iat": 0, + "iss": "string", + "nbf": 0, + "obfuscated_subject": "string", + "scope": "string", + "sub": "string", + "token_type": "string", + "username": "string" +} + +``` + +*Introspection contains an access token's session data as specified by IETF RFC 7662, see:* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|active|boolean|true|none|Active is a boolean indicator of whether or not the presented token is currently active. The specifics of a token's "active" state will vary depending on the implementation of the authorization server and the information it keeps about its tokens, but a "true" value return for the "active" property will generally indicate that a given token has been issued by this authorization server, has not been revoked by the resource owner, and is within its given time window of validity (e.g., after its issuance time and before its expiration time).| +|aud|[string]|false|none|Audience contains a list of the token's intended audiences.| +|client_id|string|false|none|ClientID is aclient identifier for the OAuth 2.0 client that requested this token.| +|exp|integer(int64)|false|none|Expires at is an integer timestamp, measured in the number of seconds since January 1 1970 UTC, indicating when this token will expire.| +|ext|object|false|none|Extra is arbitrary data set by the session.| +|» **additionalProperties**|object|false|none|none| +|iat|integer(int64)|false|none|Issued at is an integer timestamp, measured in the number of seconds since January 1 1970 UTC, indicating when this token was originally issued.| +|iss|string|false|none|IssuerURL is a string representing the issuer of this token| +|nbf|integer(int64)|false|none|NotBefore is an integer timestamp, measured in the number of seconds since January 1 1970 UTC, indicating when this token is not to be used before.| +|obfuscated_subject|string|false|none|ObfuscatedSubject is set when the subject identifier algorithm was set to "pairwise" during authorization. It is the `sub` value of the ID Token that was issued.| +|scope|string|false|none|Scope is a JSON string containing a space-separated list of scopes associated with this token.| +|sub|string|false|none|Subject of the token, as defined in JWT [RFC7519]. Usually a machine-readable identifier of the resource owner who authorized this token.| +|token_type|string|false|none|TokenType is the introspected token's type, for example `access_token` or `refresh_token`.| +|username|string|false|none|Username is a human-readable identifier for the resource owner who authorized this token.| + +oauthTokenResponse +#### oauthTokenResponse + + + +```json +{ + "access_token": "string", + "expires_in": 0, + "id_token": 0, + "refresh_token": "string", + "scope": 0, + "token_type": "string" +} + +``` + +*The token response* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|access_token|string|false|none|The access token issued by the authorization server.| +|expires_in|integer(int64)|false|none|The lifetime in seconds of the access token. For example, the value "3600" denotes that the access token will expire in one hour from the time the response was generated.| +|id_token|integer(int64)|false|none|To retrieve a refresh token request the id_token scope.| +|refresh_token|string|false|none|The refresh token, which can be used to obtain new access tokens. To retrieve it add the scope "offline" to your access token request.| +|scope|integer(int64)|false|none|The scope of the access token| +|token_type|string|false|none|The type of the token issued| + +openIDConnectContext +#### openIDConnectContext + + + +```json +{ + "acr_values": [ + "string" + ], + "display": "string", + "id_token_hint_claims": { + "property1": {}, + "property2": {} + }, + "login_hint": "string", + "ui_locales": [ + "string" + ] +} + +``` + +*Contains optional information about the OpenID Connect request.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|acr_values|[string]|false|none|ACRValues is the Authentication AuthorizationContext Class Reference requested in the OAuth 2.0 Authorization request. It is a parameter defined by OpenID Connect and expresses which level of authentication (e.g. 2FA) is required. OpenID Connect defines it as follows: > Requested Authentication AuthorizationContext Class Reference values. Space-separated string that specifies the acr values that the Authorization Server is being requested to use for processing this Authentication Request, with the values appearing in order of preference. The Authentication AuthorizationContext Class satisfied by the authentication performed is returned as the acr Claim Value, as specified in Section 2. The acr Claim is requested as a Voluntary Claim by this parameter.| +|display|string|false|none|Display is a string value that specifies how the Authorization Server displays the authentication and consent user interface pages to the End-User. The defined values are: page: The Authorization Server SHOULD display the authentication and consent UI consistent with a full User Agent page view. If the display parameter is not specified, this is the default display mode. popup: The Authorization Server SHOULD display the authentication and consent UI consistent with a popup User Agent window. The popup User Agent window should be of an appropriate size for a login-focused dialog and should not obscure the entire window that it is popping up over. touch: The Authorization Server SHOULD display the authentication and consent UI consistent with a device that leverages a touch interface. wap: The Authorization Server SHOULD display the authentication and consent UI consistent with a "feature phone" type display. The Authorization Server MAY also attempt to detect the capabilities of the User Agent and present an appropriate display.| +|id_token_hint_claims|object|false|none|IDTokenHintClaims are the claims of the ID Token previously issued by the Authorization Server being passed as a hint about the End-User's current or past authenticated session with the Client.| +|» **additionalProperties**|object|false|none|none| +|login_hint|string|false|none|LoginHint hints about the login identifier the End-User might use to log in (if necessary). This hint can be used by an RP if it first asks the End-User for their e-mail address (or other identifier) and then wants to pass that value as a hint to the discovered authorization service. This value MAY also be a phone number in the format specified for the phone_number Claim. The use of this parameter is optional.| +|ui_locales|[string]|false|none|UILocales is the End-User'id preferred languages and scripts for the user interface, represented as a space-separated list of BCP47 [RFC5646] language tag values, ordered by preference. For instance, the value "fr-CA fr en" represents a preference for French as spoken in Canada, then French (without a region designation), followed by English (without a region designation). An error SHOULD NOT result if some or all of the requested locales are not supported by the OpenID Provider.| + +rejectRequest +#### rejectRequest + + + +```json +{ + "error": "string", + "error_debug": "string", + "error_description": "string", + "error_hint": "string", + "status_code": 0 +} + +``` + +*The request payload used to accept a login or consent request.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|error|string|false|none|none| +|error_debug|string|false|none|none| +|error_description|string|false|none|none| +|error_hint|string|false|none|none| +|status_code|integer(int64)|false|none|none| + +swaggerFlushInactiveAccessTokens +#### swaggerFlushInactiveAccessTokens + + + +```json +{ + "Body": { + "notAfter": "2018-11-15T08:23:46Z" + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[flushInactiveOAuth2TokensRequest](#schemaflushinactiveoauth2tokensrequest)|false|none|none| + +swaggerJsonWebKeyQuery +#### swaggerJsonWebKeyQuery + + + +```json +{ + "kid": "string", + "set": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|kid|string|true|none|The kid of the desired key in: path| +|set|string|true|none|The set in: path| + +swaggerJwkCreateSet +#### swaggerJwkCreateSet + + + +```json +{ + "Body": { + "alg": "string", + "kid": "string", + "use": "string" + }, + "set": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[jsonWebKeySetGeneratorRequest](#schemajsonwebkeysetgeneratorrequest)|false|none|none| +|set|string|true|none|The set in: path| + +swaggerJwkSetQuery +#### swaggerJwkSetQuery + + + +```json +{ + "set": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|set|string|true|none|The set in: path| + +swaggerJwkUpdateSet +#### swaggerJwkUpdateSet + + + +```json +{ + "Body": { + "keys": [ + { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + } + ] + }, + "set": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[JSONWebKeySet](#schemajsonwebkeyset)|false|none|none| +|set|string|true|none|The set in: path| + +swaggerJwkUpdateSetKey +#### swaggerJwkUpdateSetKey + + + +```json +{ + "Body": { + "alg": "RS256", + "crv": "P-256", + "d": "T_N8I-6He3M8a7X1vWt6TGIx4xB_GP3Mb4SsZSA4v-orvJzzRiQhLlRR81naWYxfQAYt5isDI6_C2L9bdWo4FFPjGQFvNoRX-_sBJyBI_rl-TBgsZYoUlAj3J92WmY2inbA-PwyJfsaIIDceYBC-eX-xiCu6qMqkZi3MwQAFL6bMdPEM0z4JBcwFT3VdiWAIRUuACWQwrXMq672x7fMuaIaHi7XDGgt1ith23CLfaREmJku9PQcchbt_uEY-hqrFY6ntTtS4paWWQj86xLL94S-Tf6v6xkL918PfLSOTq6XCzxvlFwzBJqApnAhbwqLjpPhgUG04EDRrqrSBc5Y1BLevn6Ip5h1AhessBp3wLkQgz_roeckt-ybvzKTjESMuagnpqLvOT7Y9veIug2MwPJZI2VjczRc1vzMs25XrFQ8DpUy-bNdp89TmvAXwctUMiJdgHloJw23Cv03gIUAkDnsTqZmkpbIf-crpgNKFmQP_EDKoe8p_PXZZgfbRri3NoEVGP7Mk6yEu8LjJhClhZaBNjuWw2-KlBfOA3g79mhfBnkInee5KO9mGR50qPk1V-MorUYNTFMZIm0kFE6eYVWFBwJHLKYhHU34DoiK1VP-svZpC2uAMFNA_UJEwM9CQ2b8qe4-5e9aywMvwcuArRkAB5mBIfOaOJao3mfukKAE", + "dp": "G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oimYwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_NmtuYZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0", + "dq": "s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUUvMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk", + "e": "AQAB", + "k": "GawgguFyGrWKav7AX4VKUg", + "kid": "1603dfe0af8f4596", + "kty": "RSA", + "n": "vTqrxUyQPl_20aqf5kXHwDZrel-KovIp8s7ewJod2EXHl8tWlRB3_Rem34KwBfqlKQGp1nqah-51H4Jzruqe0cFP58hPEIt6WqrvnmJCXxnNuIB53iX_uUUXXHDHBeaPCSRoNJzNysjoJ30TIUsKBiirhBa7f235PXbKiHducLevV6PcKxJ5cY8zO286qJLBWSPm-OIevwqsIsSIH44Qtm9sioFikhkbLwoqwWORGAY0nl6XvVOlhADdLjBSqSAeT1FPuCDCnXwzCDR8N9IFB_IjdStFkC-rVt2K5BYfPd0c3yFp_vHR15eRd0zJ8XQ7woBC8Vnsac6Et1pKS59pX6256DPWu8UDdEOolKAPgcd_g2NpA76cAaF_jcT80j9KrEzw8Tv0nJBGesuCjPNjGs_KzdkWTUXt23Hn9QJsdc1MZuaW0iqXBepHYfYoqNelzVte117t4BwVp0kUM6we0IqyXClaZgOI8S-WDBw2_Ovdm8e5NmhYAblEVoygcX8Y46oH6bKiaCQfKCFDMcRgChme7AoE1yZZYsPbaG_3IjPrC4LBMHQw8rM9dWjJ8ImjicvZ1pAm0dx-KHCP3y5PVKrxBDf1zSOsBRkOSjB8TPODnJMz6-jd5hTtZxpZPwPoIdCanTZ3ZD6uRBpTmDwtpRGm63UQs1m5FWPwb0T2IF0", + "p": "6NbkXwDWUhi-eR55Cgbf27FkQDDWIamOaDr0rj1q0f1fFEz1W5A_09YvG09Fiv1AO2-D8Rl8gS1Vkz2i0zCSqnyy8A025XOcRviOMK7nIxE4OH_PEsko8dtIrb3TmE2hUXvCkmzw9EsTF1LQBOGC6iusLTXepIC1x9ukCKFZQvdgtEObQ5kzd9Nhq-cdqmSeMVLoxPLd1blviVT9Vm8-y12CtYpeJHOaIDtVPLlBhJiBoPKWg3vxSm4XxIliNOefqegIlsmTIa3MpS6WWlCK3yHhat0Q-rRxDxdyiVdG_wzJvp0Iw_2wms7pe-PgNPYvUWH9JphWP5K38YqEBiJFXQ", + "q": "0A1FmpOWR91_RAWpqreWSavNaZb9nXeKiBo0DQGBz32DbqKqQ8S4aBJmbRhJcctjCLjain-ivut477tAUMmzJwVJDDq2MZFwC9Q-4VYZmFU4HJityQuSzHYe64RjN-E_NQ02TWhG3QGW6roq6c57c99rrUsETwJJiwS8M5p15Miuz53DaOjv-uqqFAFfywN5WkxHbraBcjHtMiQuyQbQqkCFh-oanHkwYNeytsNhTu2mQmwR5DR2roZ2nPiFjC6nsdk-A7E3S3wMzYYFw7jvbWWoYWo9vB40_MY2Y0FYQSqcDzcBIcq_0tnnasf3VW4Fdx6m80RzOb2Fsnln7vKXAQ", + "qi": "GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzgUIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rxyR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU", + "use": "sig", + "x": "f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU", + "x5c": [ + "string" + ], + "y": "x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0" + }, + "kid": "string", + "set": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[JSONWebKey](#schemajsonwebkey)|false|none|none| +|kid|string|true|none|The kid of the desired key in: path| +|set|string|true|none|The set in: path| + +swaggerOAuthIntrospectionRequest +#### swaggerOAuthIntrospectionRequest + + + +```json +{ + "scope": "string", + "token": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|scope|string|false|none|An optional, space separated list of required scopes. If the access token was not granted one of the scopes, the result of active will be false. in: formData| +|token|string|true|none|The string value of the token. For access tokens, this is the "access_token" value returned from the token endpoint defined in OAuth 2.0 [RFC6749], Section 5.1. This endpoint DOES NOT accept refresh tokens for validation.| + +swaggerRevokeOAuth2TokenParameters +#### swaggerRevokeOAuth2TokenParameters + + + +```json +{ + "token": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|token|string|true|none|in: formData| + +userinfoResponse +#### userinfoResponse + + + +```json +{ + "birthdate": "string", + "email": "string", + "email_verified": true, + "family_name": "string", + "gender": "string", + "given_name": "string", + "locale": "string", + "middle_name": "string", + "name": "string", + "nickname": "string", + "phone_number": "string", + "phone_number_verified": true, + "picture": "string", + "preferred_username": "string", + "profile": "string", + "sub": "string", + "updated_at": 0, + "website": "string", + "zoneinfo": "string" +} + +``` + +*The userinfo response* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|birthdate|string|false|none|End-User's birthday, represented as an ISO 8601:2004 [ISO8601‑2004] YYYY-MM-DD format. The year MAY be 0000, indicating that it is omitted. To represent only the year, YYYY format is allowed. Note that depending on the underlying platform's date related function, providing just year can result in varying month and day, so the implementers need to take this factor into account to correctly process the dates.| +|email|string|false|none|End-User's preferred e-mail address. Its value MUST conform to the RFC 5322 [RFC5322] addr-spec syntax. The RP MUST NOT rely upon this value being unique, as discussed in Section 5.7.| +|email_verified|boolean|false|none|True if the End-User's e-mail address has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this e-mail address was controlled by the End-User at the time the verification was performed. The means by which an e-mail address is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating.| +|family_name|string|false|none|Surname(s) or last name(s) of the End-User. Note that in some cultures, people can have multiple family names or no family name; all can be present, with the names being separated by space characters.| +|gender|string|false|none|End-User's gender. Values defined by this specification are female and male. Other values MAY be used when neither of the defined values are applicable.| +|given_name|string|false|none|Given name(s) or first name(s) of the End-User. Note that in some cultures, people can have multiple given names; all can be present, with the names being separated by space characters.| +|locale|string|false|none|End-User's locale, represented as a BCP47 [RFC5646] language tag. This is typically an ISO 639-1 Alpha-2 [ISO639‑1] language code in lowercase and an ISO 3166-1 Alpha-2 [ISO3166‑1] country code in uppercase, separated by a dash. For example, en-US or fr-CA. As a compatibility note, some implementations have used an underscore as the separator rather than a dash, for example, en_US; Relying Parties MAY choose to accept this locale syntax as well.| +|middle_name|string|false|none|Middle name(s) of the End-User. Note that in some cultures, people can have multiple middle names; all can be present, with the names being separated by space characters. Also note that in some cultures, middle names are not used.| +|name|string|false|none|End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences.| +|nickname|string|false|none|Casual name of the End-User that may or may not be the same as the given_name. For instance, a nickname value of Mike might be returned alongside a given_name value of Michael.| +|phone_number|string|false|none|End-User's preferred telephone number. E.164 [E.164] is RECOMMENDED as the format of this Claim, for example, +1 (425) 555-1212 or +56 (2) 687 2400. If the phone number contains an extension, it is RECOMMENDED that the extension be represented using the RFC 3966 [RFC3966] extension syntax, for example, +1 (604) 555-1234;ext=5678.| +|phone_number_verified|boolean|false|none|True if the End-User's phone number has been verified; otherwise false. When this Claim Value is true, this means that the OP took affirmative steps to ensure that this phone number was controlled by the End-User at the time the verification was performed. The means by which a phone number is verified is context-specific, and dependent upon the trust framework or contractual agreements within which the parties are operating. When true, the phone_number Claim MUST be in E.164 format and any extensions MUST be represented in RFC 3966 format.| +|picture|string|false|none|URL of the End-User's profile picture. This URL MUST refer to an image file (for example, a PNG, JPEG, or GIF image file), rather than to a Web page containing an image. Note that this URL SHOULD specifically reference a profile photo of the End-User suitable for displaying when describing the End-User, rather than an arbitrary photo taken by the End-User.| +|preferred_username|string|false|none|Non-unique shorthand name by which the End-User wishes to be referred to at the RP, such as janedoe or j.doe. This value MAY be any valid JSON string including special characters such as @, /, or whitespace.| +|profile|string|false|none|URL of the End-User's profile page. The contents of this Web page SHOULD be about the End-User.| +|sub|string|false|none|Subject - Identifier for the End-User at the IssuerURL.| +|updated_at|integer(int64)|false|none|Time the End-User's information was last updated. Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the date/time.| +|website|string|false|none|URL of the End-User's Web page or blog. This Web page SHOULD contain information published by the End-User or an organization that the End-User is affiliated with.| +|zoneinfo|string|false|none|String from zoneinfo [zoneinfo] time zone database representing the End-User's time zone. For example, Europe/Paris or America/Los_Angeles.| + +version +#### version + + + +```json +{ + "version": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|version|string|false|none|Version is the service's version.| + +wellKnown +#### wellKnown + + + +```json +{ + "authorization_endpoint": "https://playground.ory.sh/ory-hydra/public/oauth2/auth", + "claims_parameter_supported": true, + "claims_supported": [ + "string" + ], + "grant_types_supported": [ + "string" + ], + "id_token_signing_alg_values_supported": [ + "string" + ], + "issuer": "https://playground.ory.sh/ory-hydra/public/", + "jwks_uri": "https://playground.ory.sh/ory-hydra/public/.well-known/jwks.json", + "registration_endpoint": "https://playground.ory.sh/ory-hydra/admin/client", + "request_parameter_supported": true, + "request_uri_parameter_supported": true, + "require_request_uri_registration": true, + "response_modes_supported": [ + "string" + ], + "response_types_supported": [ + "string" + ], + "scopes_supported": [ + "string" + ], + "subject_types_supported": "public, pairwise", + "token_endpoint": "https://playground.ory.sh/ory-hydra/public/oauth2/token", + "token_endpoint_auth_methods_supported": [ + "string" + ], + "userinfo_endpoint": "string", + "userinfo_signing_alg_values_supported": [ + "string" + ] +} + +``` + +*WellKnown represents important OpenID Connect discovery metadata* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|authorization_endpoint|string|true|none|URL of the OP's OAuth 2.0 Authorization Endpoint.| +|claims_parameter_supported|boolean|false|none|Boolean value specifying whether the OP supports use of the claims parameter, with true indicating support.| +|claims_supported|[string]|false|none|JSON array containing a list of the Claim Names of the Claims that the OpenID Provider MAY be able to supply values for. Note that for privacy or other reasons, this might not be an exhaustive list.| +|grant_types_supported|[string]|false|none|JSON array containing a list of the OAuth 2.0 Grant Type values that this OP supports.| +|id_token_signing_alg_values_supported|[string]|true|none|JSON array containing a list of the JWS signing algorithms (alg values) supported by the OP for the ID Token to encode the Claims in a JWT.| +|issuer|string|true|none|URL using the https scheme with no query or fragment component that the OP asserts as its IssuerURL Identifier. If IssuerURL discovery is supported , this value MUST be identical to the issuer value returned by WebFinger. This also MUST be identical to the iss Claim value in ID Tokens issued from this IssuerURL.| +|jwks_uri|string|true|none|URL of the OP's JSON Web Key Set [JWK] document. This contains the signing key(s) the RP uses to validate signatures from the OP. The JWK Set MAY also contain the Server's encryption key(s), which are used by RPs to encrypt requests to the Server. When both signing and encryption keys are made available, a use (Key Use) parameter value is REQUIRED for all keys in the referenced JWK Set to indicate each key's intended usage. Although some algorithms allow the same key to be used for both signatures and encryption, doing so is NOT RECOMMENDED, as it is less secure. The JWK x5c parameter MAY be used to provide X.509 representations of keys provided. When used, the bare key values MUST still be present and MUST match those in the certificate.| +|registration_endpoint|string|false|none|URL of the OP's Dynamic Client Registration Endpoint.| +|request_parameter_supported|boolean|false|none|Boolean value specifying whether the OP supports use of the request parameter, with true indicating support.| +|request_uri_parameter_supported|boolean|false|none|Boolean value specifying whether the OP supports use of the request_uri parameter, with true indicating support.| +|require_request_uri_registration|boolean|false|none|Boolean value specifying whether the OP requires any request_uri values used to be pre-registered using the request_uris registration parameter.| +|response_modes_supported|[string]|false|none|JSON array containing a list of the OAuth 2.0 response_mode values that this OP supports.| +|response_types_supported|[string]|true|none|JSON array containing a list of the OAuth 2.0 response_type values that this OP supports. Dynamic OpenID Providers MUST support the code, id_token, and the token id_token Response Type values.| +|scopes_supported|[string]|false|none|SON array containing a list of the OAuth 2.0 [RFC6749] scope values that this server supports. The server MUST support the openid scope value. Servers MAY choose not to advertise some supported scope values even when this parameter is used| +|subject_types_supported|[string]|true|none|JSON array containing a list of the Subject Identifier types that this OP supports. Valid types include pairwise and public.| +|token_endpoint|string|true|none|URL of the OP's OAuth 2.0 Token Endpoint| +|token_endpoint_auth_methods_supported|[string]|false|none|JSON array containing a list of Client Authentication methods supported by this Token Endpoint. The options are client_secret_post, client_secret_basic, client_secret_jwt, and private_key_jwt, as described in Section 9 of OpenID Connect Core 1.0| +|userinfo_endpoint|string|false|none|URL of the OP's UserInfo Endpoint.| +|userinfo_signing_alg_values_supported|[string]|false|none|JSON array containing a list of the JWS [JWS] signing algorithms (alg values) [JWA] supported by the UserInfo Endpoint to encode the Claims in a JWT [JWT].| + diff --git a/docs/hydra/sdk/go.md b/docs/hydra/sdk/go.md new file mode 100644 index 000000000..8ca0eb341 --- /dev/null +++ b/docs/hydra/sdk/go.md @@ -0,0 +1,70 @@ +--- +id: hydra-sdk-go +title: Go +--- + +To install the Go SDK, run: + +``` +go get -u -d github.com/ory/hydra/sdk/go/hydra +``` + +### Configuration + +The Go SDK is auto generated from swagger but contains some helpers, such as `NewSDK`: + +```go +import "github.com/ory/hydra/sdk/go/hydra" + +sdk, err := hydra.NewSDK(&hydra.Configuration{ + AdminURL: "https://hydra.localhost:4445", +}) +``` + +#### With OAuth 2.0 + +If you want to send an OAuth 2.0 Access Token while accessing ORY Hydra's Admin API (e.g. because you protect it using +ORY Oathkeeper), you can configure the SDK to do that: + +```go +import "github.com/ory/hydra/sdk/go/hydra" + +sdk, err := hydra.NewSDK(&hydra.Configuration{ + AdminURL: "https://hydra.localhost:4445", + PublicURL: "https://hydra.localhost:4444", + Clientid: hydra-sdk-"...", + ClientSecret: "...", + Scopes: []string{"..."}, +}) +``` + +### API Usage + +APIs usually have three return values. Please check for errors as well as status codes! + +```go +client, response, error := sdk.CreateClient(swagger.Client{ /* .... payload .... */}) +if err != nil { + // This usually indicates a network error. +} else if response.StatusCode != http.StatusCreated { + // If the status code is not 2xx, something went wrong on the application level (e.g. wrong credentials, database offline, ...) +} + +fmt.Printf("Client created: %+v", client) +``` + +In rare cases, methods have only two return values. This happens when the REST API returns `204 No Content`: + +``` +response, err := sdk.DeleteClient("client-id") +if err != nil { + // This usually indicates a network error. +} else if response.StatusCode != http.StatusNoContent { + // If the status code is not 2xx, something went wrong on the application level (e.g. wrong credentials, database offline, ...) +} +``` + +### API Docs + +API docs are available [here](https://github.com/ory/hydra/blob/master/sdk/go/hydra/swagger/README.md). +Please note that those docs are generated and may introduce bugs if code examples are used 1:1. diff --git a/docs/hydra/sdk/index.md b/docs/hydra/sdk/index.md new file mode 100644 index 000000000..40e222e2c --- /dev/null +++ b/docs/hydra/sdk/index.md @@ -0,0 +1,24 @@ +--- +id: hydra-sdk-index +title: Overview +--- + +All SDKs are use automated code generation provided by [`swagger-codegen`](https://github.com/swagger-api/swagger-codegen). +Unfortunately, `swagger-codegen` has serious breaking changes in the generated code when upgrading versions. Therefore, +we do not make backwards compatibility promises with regards to the generated SDKs. We hope to improve this process +in the future. + +Before you check out the SDKs, head over to the [REST API](api.md) documentation which includes code samples for common +programming languages for each REST endpoint. + +> The SDKs do not provide a good API for dealing with OAuth 2.0 Flows (e.g. Authorize Code Flow, Refresh Flow, ...). +But there are tons of [libraries available for consuming OAuth 2.0](https://oauth.net/code/). Do not write your +own OAuth 2.0 Library! + +If you want to use the SDK despite its caveats (mediocre code quality, potential breaking changes), you will find more information here: + +* [Golang](go.md) +* [JavaScript](js.md) + +Missing your programming language? [Create an issue](https://github.com/ory/hydra/issues) and help us build, +test and publish the SDK for your programming language! diff --git a/docs/hydra/sdk/js.md b/docs/hydra/sdk/js.md new file mode 100644 index 000000000..050e5e6af --- /dev/null +++ b/docs/hydra/sdk/js.md @@ -0,0 +1,50 @@ +--- +id: hydra-sdk-js +title: JavaScript +--- + +> The JavaScript SDK is autogenerated and developer experience is not great. We encourage you to +use [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) instead. + +To install the JavaScript SDK, run: + +``` +npm install --save ory-hydra-sdk +``` + +### Configuration + +#### Basic configuration + +```js +const Hydra = require('ory-hydra-sdk') + +// Set this to Hydra's URL +Hydra.ApiClient.instance.basePath = 'http://localhost:4445' + +``` + +### API Usage + +```js +const hydra = new Hydra.AdminAPI() + +// for example, let's fetch all OAuth2 clients +hydra.listOAuth2Clients((error, data, response) => { + if (error) { + // a network error occurred. + throw error + } else if (response.statusCode < 200 || response.statusCode >= 400) { + // an application error occurred. + throw new Error('Consent endpoint gave status code ' + response.statusCode + ', but status code 200 was expected.') + } + + console.log(response) // a list of OAuth2 clients. +}) +``` + +### API Docs + +API docs are available [here](https://github.com/ory/hydra/blob/master/sdk/js/swagger/README.md). +Please note that those docs are generated and may introduce bugs if code examples are used 1:1. Especially +the package name is not correct. diff --git a/docs/hydra/security-architecture.md b/docs/hydra/security-architecture.md new file mode 100644 index 000000000..cc994bfa6 --- /dev/null +++ b/docs/hydra/security-architecture.md @@ -0,0 +1,105 @@ +--- +id: hydra-security-architecture +title: Security Architecture +--- + +Hydra is built with tough security in mind. + + + +## OAuth 2.0 Security Overview + +Hydra is an implementation of the security-first Fosite OAuth 2.0 SDK +([https://github.com/ory/fosite](https://github.com/ory/fosite)). Fosite respects +the [OAuth 2.0 Threat Model and Security Considerations](https://tools.ietf.org/html/rfc6819#section-5.1.5.3) by +the IETF, specifically: + +- No Cleartext Storage of Credentials +- Encryption of Credentials +- Use Short Expiration Time +- Limit Number of Usages or One-Time Usage +- Bind Token to Client id +- Automatic Revocation of Derived Tokens If Abuse Is Detected +- Binding of Refresh Token to "client_id" +- Refresh Token Rotation +- Revocation of Refresh Tokens +- Validate Pre-Registered "redirect_uri" +- Binding of Authorization "code" to "client_id" +- Binding of Authorization "code" to "redirect_uri" +- Opaque access tokens +- Opaque refresh tokens +- Ensure Confidentiality of Requests +- Use of Asymmetric Cryptography +- Enforcing random states: Without a random-looking state or OpenID Connect nonce the request will fail. + +Additionally these safeguards are implemented: + +- Advanced Token Validation: Tokens are laid out as <key>.<signature> where <signature> +is created using HMAC-SHA256 using a global secret. + +### Advanced Token Validation (Datastore Security) + +For a OAuth2 access token, refresh token or authorize code to be valid, one requires both the key and +the signature (formatted as <key>.<signature>). Only the signature is stored in the datastore (SQL), +thus a compromised datastore will not allow an attacker to gain access to any valid authorize codes, +access tokens, or refresh tokens. + +Because HMAC-SHA256 is used, the System Secret is required to create valid key-signature pairs, rendering an attacker +unable to inject new codes or tokens into a compromised datastore. + +## Cryptography + +Hydra uses different cryptographic methods, this is an overview of all of them. + +### AES-GCM + +AES-GCM is used to encrypt JWKs at rest using a key size of 256 bit which exceeds requirements by Lenstra, +ECRYPT II, NIST, ANSSI, and BSI, see [https://www.keylength.com/en/compare/](https://www.keylength.com/en/compare/). + +GCM (Galois/Counter Mode) is an authenticated encryption algorithm designed to provide both data authenticity +(integrity) and confidentiality. GCM uses a nonce (“IV”) that has an upper limit of 2^32 nonces. If more +nonces are used, there is risk of repeats. This means that you risk collisions when storing more than 2^32 +documents authenticated with GCM. Because AES-GCM is only used to encrypt data at rest, this is might +only impose a problem if + +1. more than 2^32 documents are stored using AES-GCM +2. an attacker gains access to the datastore where > 2^32 documents are stored +3. the attacker is able to exploit repeats, for example by authenticating malicious documents + +### RS256 + +RSASSA-PKCS1-v1_5 using SHA-256 (RS256) is used to sign JWTs. It’s use is recommended by the JWA +specification, see [https://www.rfc-editor.org/rfc/rfc7518.txt](https://www.rfc-editor.org/rfc/rfc7518.txt) + +The RSA Key size is 4096 bit long, exceeding the minimum requirement of 2048 bit by +[https://www.rfc-editor.org/rfc/rfc7518.txt](https://www.rfc-editor.org/rfc/rfc7518.txt). + +Recommendations from NIST, ANSSI, IAD-NSA, BSI, Lenstra and others vary between 1300 and 2048 bit key +lengths for asymmetric cryptography based on discrete logarithms (RSA). 4096 exceeds all recommendations +for 2017 from all authorities, see [https://www.keylength.com/en/compare/](https://www.keylength.com/en/compare/). + +### HMAC-SHA256 + +HMAC (FIPS 198) with SHA256 (FIPS 180-4) is used to sign access tokens, authorize codes and refresh +tokens. SHA-2 (with 256 bit) is encouraged by NIST, see +[http://csrc.nist.gov/groups/ST/hash/policy.html](http://csrc.nist.gov/groups/ST/hash/policy.html) + +### BCrypt + +BCrypt is used to hash client credentials at rest. It is not officially recommended by NIST as +it is not based on hashing primitives such as SHA-2, but rather on Blowfish. However, BCrypt is +much stronger than any other (salted) hashing method for passwords, has wide adoption +and is an official golang/x library. + +I recommend reading this thread on Security Stack Exchange on BCrypt, SCrypt +and PBKDF2: [https://security.stackexchange.com/questions/4781/do-any-security-experts-recommend-bcrypt-for-password-storage](https://security.stackexchange.com/questions/4781/do-any-security-experts-recommend-bcrypt-for-password-storage) + +Be aware that BCrypt causes very high CPU loads, depending on the Workload Factor. We +strongly advise reducing the number of requests that use Basic Authorization. + +## How does Access Control work with Hydra? + +Hydra supports two concepts of authorization. One is called Token Introspection which is a standard +by the IETF ( [https://tools.ietf.org/html/rfc7662](https://tools.ietf.org/html/rfc7662) ). It is primarily +targeted at third-party services and is usually used by a programmatic API. It can be used by first-party +services too. The sole purpose of this endpoint is to check whether an access token is valid or not. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 000000000..40ef3ee59 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,41 @@ +--- +id: index +title: ORY Developer Documentation +--- + +## GitHub + +You can find our open source projects on GitHub: + +* [ORY Hydra](https://github.com/ory/hydra) +* [ORY Oathkeeper](https://github.com/ory/oathkeeper) +* [ORY Keto](https://github.com/ory/keto) +* [ORY Fosite](https://github.com/ory/fosite) +* [ORY Ladon](https://github.com/ory/ladon) +* [ORY Dockertest](https://github.com/ory/dockertest) + +## REST API Docs + +While this developer guide focuses on explaining concepts, +the concrete REST APIs are documented using swagger and +can be viewed here: + +* [ORY Hydra](./hydra/sdk/api.md) +* [ORY Oathkeeper](./oathkeeper/sdk/api.md) +* [ORY Keto](./keto/sdk/api.md) + +## SDK Docs + +Our services come with auto-generated client SDKs: + +* [ORY Hydra](https://github.com/ory/hydra/tree/master/sdk) +* [ORY Oathkeeper](https://github.com/ory/oathkeeper/tree/master/sdk) +* [ORY Keto](https://github.com/ory/keto/tree/master/sdk) + +## GoDoc + +GoDoc documents Go code and is available for our Go libraries: + +* [ORY Fosite](https://godoc.org/github.com/ory/fosite) +* [ORY Dockertest](https://godoc.org/github.com/ory/dockertest) +* [ORY Ladon](https://godoc.org/github.com/ory/ladon) diff --git a/docs/keto/1-overview/1-policies.md b/docs/keto/1-overview/1-policies.md new file mode 100644 index 000000000..2f3efbcc7 --- /dev/null +++ b/docs/keto/1-overview/1-policies.md @@ -0,0 +1,185 @@ +--- +id: keto-overview-1-policies +title: Access Control Policies +--- + +## Overview + +If you have ever worked with cloud providers, you have probably encountered IAM (Identity & Access Management) Policies. +Amazon Web Services' IAM services is the champion of enterprise IT which can be attributed to +[AWS IAM Policies](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html). + +ORY Keto uses our [Go SDK ORY Ladon](https://github.com/ory/ladon) to provide a self-hosted service that implements +a comparable experience to AWS IAM Policies, or more generally, Access Control Policies. + +## Users & Permissions + +Before we take a look at Access Control Policies in detail, let's get some of the basics figured out. Every app that +has users usually assigns permissions to these users ("Bob, Alice are allowed to write blog posts"). There are +various established practices for assigning one or more permissions to one or more users. + +In the context of access control, you'll often encounter **identities** or **subjects** as an alias for users. In this +documentation we use the terminology **subject** as it summarizes users, robots, cronjobs, services, ... best. So +whenever you read "subject" you can substitute it with users, if it helps your understanding. + +Let's begin with a short overview of the established concepts surrounding permissions. Please be aware that the next +sections are merely an overview of those topics and aimed at giving you some context. They do not explain all the +nuances of each respective approach. + +### Access Control Lists (ACL) + +An [Access Control List (ACL)](https://en.wikipedia.org/wiki/Access_control_list) is a matrix of users and permissions: + +| | blog_post.create | blog_post.delete | blog_post.modify | blog_post.read | +|-------|------------------|------------------|------------------|----------------| +| alice | yes | yes | yes | yes | +| bob | no | no | no | yes | +| peter | yes | no | yes | yes | + +In the example above, `alice` has the permission to create a blog post `(blog_post.create)` while bob des not. All three +(alice, bob, peter) can read blog posts. + +Similarly, you can create a matrix of resources (e.g. blog articles) and each user's permissions +(`c` for `create`, `m` for `modify`, ...) with regards to that resource: + +| | blog_post.1 | blog_post.2 | blog_post.3 | blog_post.4 | +|------- |------------- |------------- |------------- |------------- | +| alice | c,r,m,d | c,r,m,d | c,r,m,d | c,r,m,d | +| bob | r | r | r | r | +| peter | c,r,m,d | r | c,r,m,d | r | + +ACLs are common in filesystems (`chmod` / `chown`) and in applications with few subjects. However, the matrix becomes +unreadable and unmanagable if you have thousands or even millions of subjects. Therefore, ACLs are rarely used in +web applications. + +### Role Based Access Control (RBAC) + +[Role Based Access Control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) maps subjects to roles +and roles to permissions. The goal of RBAC is to make permission management convenient by grouping subjects +in roles and assigning permissions roles. This type of access control is very common in web applications, where you often +encounter roles such as "administrator", "moderator", and so on. + +What's common in RBAC is that roles can inherit permissions from one another. The role administrator, for example, +could inherit all permissions from role moderator. This further decreases management complexity as, instead of adding +all permissions to administrator or assigning a user to both moderator and administrator roles, you simply point the +administrator role to inherit from the moderator one. + +Let's come back to alice, bob, peter, and blog posts and the matrix from the ACL example, but this time we define +roles "reader", "author", "admin" and model the ACL example using RBAC: + +![RBAC Example](../../../images/docs/keto/rbac.png). + +As you can see, `admin` inherits from `author`, which inherits from `reader`. Only `alice` (or rather `admin`) can delete blog posts, +whereas `author` can create and modify blog posts. We assign the roles to our subjects `bob`, `peter`, `alice` and +express the same permissions as in the ACL example. + +RBAC is everywhere. If you ever installed a forum software such as [phpBB](https://www.phpbb.com/support/docs/en/3.1/ug/adminguide/permissions_roles/), +[Wordpress](https://codex.wordpress.org/Roles_and_Capabilities) or others, you have definitely encountered ACL, RBAC, or both. + +RBAC reduces management complexity & overhead with large user/subject bases. Sometimes however, RBAC is not enough as well. +That's the case when you're trying to express ownership (e.g. `bob` can modify blog posts, but only his own), or +have attributes (e.g. `bob` works in department `blog`), or multi-tenant environments. + +### Access Control Policies (ACP) + +Access Control Policies (usually JSON "documents") define an `effect`, `subject`, `action`, and `resource`. For example, `alice` (subject) is +`allowed` (effect) to `delete` (action) blog article with ID `my-first-blog-post` (`resource`). This is very similar +to how ACLs work: + +```json +{ + "subjects": ["alice"], + "resources": ["blog_posts:my-first-blog-post"], + "actions": ["delete"] + "effect": "allow" +} +``` + +The policy above allows `alice` to `delete` `blog_posts:my-first-blog-post`. We could apply this policy to more subjects +and also more actions or resources, if we want to: + +```json +{ + "subjects": ["alice", "bob"], + "resources": ["blog_posts:my-first-blog-post", "blog_posts:2", "blog_posts:3"], + "actions": ["delete", "create", "read", "modify"] + "effect": "allow" +} +``` + +Well, this looks like ACL in disguise so far. So what's different? + +#### Precedence + +The first difference is that we can explicitly deny access: + +```json +{ + "subjects": ["peter"], + "resources": ["blog_posts:my-first-blog-post", "blog_posts:2", "blog_posts:3"], + "actions": ["delete", "create", "read", "modify"] + "effect": "deny" +} +``` + +The policy decision point (the one checking if something is allowed or not) applies the following rule set when deciding if something is allowed or not: + +1. If at least one policy for a given subject, action, and resource matches, and the effect is `deny`, the request is always denied. +2. If no policy matches with effect `deny`, but at least one policy with effect `allow`, the request is allowed. +3. If no policy matches at all, the request is denied. + +#### Pattern Matching with Regular Expressions + +ORY Keto allows you to apply pattern matching with regular expressions as well. Depending on how you name your subjects, +resources, and actions (for more on that topic go to the [Best Practices](./4-best-practices.md) section), you can apply +pattern matching using regular expressions. + +```json +{ + "subjects": ["users:<.*>"] +} +``` + +In the example above, the (incomplete) policy would match every subject that is prefixed with `users:`, so for example +`users:alice`, `users:bob`. In ORY Ladon / ORY Keto, regular expressions are delimited with `<` and `>`. +For example, `"users:.*"` is not a valid regular expression, just a simple string. + +The next example will allow all subjects with prefix `user:` to read (`actions:read`) all resources that match `resources:blog_posts:<[0-9]+>` +(e.g. `resources:blog_posts:1234` but not `resources:blog_posts:abcde`): + +```json +{ + "subjects": ["users:<.*>"], + "resources": ["resources:blog_posts:<[0-9]+>"], + "actions": ["actions:read"] + "effect": "allow" +} +``` + +#### Conditions + +Conditions are another powerful concept. So far, we covered that an Access Control Policies applies to a list of +`subjects`, `resources`, and `actions`. Conditions narrow down the use cases in which a certain ACP applies. A condition +may, for example, mandate that the IP Address of the client making the request has to match e.g. `192.168.0.0/16` or that +the subject is also the owner of the resource: + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "remoteIPAddress": { + "type": "CIDRCondition", + "options": { + "cidr": "192.168.0.0/16" + } + } + } +} +``` + +What conditions you can use and how you use is documented [here](./5-conditions.md). Since that requires knowledge of the Warden API +works, let's look at that first in the [next chapter](./2-warden.md). diff --git a/docs/keto/1-overview/2-warden.md b/docs/keto/1-overview/2-warden.md new file mode 100644 index 000000000..38d0c163e --- /dev/null +++ b/docs/keto/1-overview/2-warden.md @@ -0,0 +1,187 @@ +--- +id: keto-overview-2-warden +title: Warden API +--- + +So far you learned how Access Control Policies work and how they are different from RBAC and ACL. Now, you will learn +how to use the Warden API to perform access control (decide if a request should be allowed or not). + +Before making a decision, we have to authenticate the subject. ORY Keto supports different authentication mechanisms +explained in the later sections. But first we'll look at how access requests work if we use "plaintext" authentication. + +Let's take a very simple policy: + +```json +{ + "subjects": ["alice"], + "resources": ["blog_posts:my-first-blog-post"], + "actions": ["delete"] + "effect": "allow" +} +``` + +By the way, you can create this policy with the ORY Keto CLI: + +``` +# We assume that your keto server runs at http://your-keto-server +$ keto policies create --endpoint http://your-keto-server \ + -a delete \ + -s alice \ + -r "blog_posts:my-first-blog-post" + --allow +``` + +Ok, so let's say a request hits our blog post API. The subject (user) that made the request is `bob` and `bob` wants +to delete the blog post `blog_posts:my-first-blog-post`. You will make a REST request to ORY Keto's Warden API: + +``` +$ curl -X POST http://your-keto-server/warden/subjects/authorize \ + --header "Content-Type: application/json" \ + -d @- << EOF + +{ + "subject": "bob", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +EOF +``` + +In this case, the response would be `{ "allowed": false }` because no policy matching `bob`, `delete`, +`blog_posts:my-first-blog-post` was found. If we were to make the same Warden API requests with alice + +``` +# curl -X POST http://your-keto-server/warden/subjects/authorize \ + --header "Content-Type: application/json" \ + -d @- << EOF + +{ + "subject": "alice", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +EOF +``` + +the response would be true because a policy was found and the effect was `allow`: `{ "allowed": true }`. You can +use the ORY Keto CLI to make the same request: + +``` +$ keto warden authorize subject --endpoint http://your-keto-server \ + --action delete \ + --resource "blog_posts:my-first-blog-post" \ + --subject alice +``` + +## Authentication + +ORY Keto supports different authentication methods at the Warden API. So far, we used the `subject` authenticator +which does not authenticate at all but simply takes the value of `subject` key in the request body. Currently supported +authenticators are: + +* `/warden/subjects/authorize`: The subject ("plaintext") authenticator we already used. +* `/warden/oauth2/access-tokens/authorize`: Validates OAuth 2.0 Access Token using the OAuth 2.0 Token Introspection +standard to resolve Access Tokens to subjects. +* `/warden/oauth2/clients/authorize`: Validates OAuth 2.0 Client Credentials using the OAuth 2.0 Client Credentials +Grant to authenticate OAuth 2.0 Clients. + +We intend on adding more authenticators such as JSON Web Tokens, SAML Assertions, and others in time. + +Let's take a deeper look at all of the available authenticators + +### Subject Authenticator + +You know this one already. The endpoint is located at `/warden/subjects/authorize` and you use request bodies such +as: + +``` +{ + "subject": "alice", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +The `subject` key is used as `subject` when looking for policies. No magic here, it's like authenticating with just +a username. + +### OAuth 2.0 Access Token Authenticator + +This authenticator is located at `/warden/oauth2/access-tokens/authorize` and is able to validate OAuth 2.0 Access Tokens +using the OAuth 2.0 Token Introspection standard. Instead of supplying a `subject`, you add an OAuth 2.0 Token and +optionally the OAuth 2.0 Scope to the request payload: + +``` +{ + "token": "some.access.token.57jgoi19g", + "scope": ["scope-a", "scope-b"], + + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +Let's assume for a second, that the token `some.access.token.57jgoi19g` was granted by subject `alice` with OAuth 2.0 Scope +`scope-a` and `scope-b`. In this case, the OAuth 2.0 Token Introspection endpoint would return a positive response (the token +is valid) and also the subject `alice`. So this authenticator looks for a policy that matches: + +``` +{ + "subject": "alice", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +#### Configuration + +You can configure this authenticator using the following environment variables: + +* `AUTHENTICATOR_OAUTH2_INTROSPECTION_URL`: The URL of the OAuth 2.0 Introspection Endpoint (usually `/oauth2/introspection`). +* `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE_STRATEGY`: Since you can include a `scope` in the request body, ORY Keto +allows you to configure a OAuth 2.0 strategy that defines how to compare OAuth 2.0 Scope: + * `hierarchic`: Scope `foo` matches `foo`, `foo.bar`, `foo.baz` but not `bar` + * `wildcard`: Scope `foo.*` matches `foo`, `foo.bar`, `foo.baz` but not `bar`. Scope `foo` matches `foo` but not `foo.bar` nor `bar` + * `exact`: Scope `foo` matches `foo` but not `bar` nor `foo.bar` +* In cases where the OAuth 2.0 Introspection endpoint is protected using OAuth 2.0 Access Tokens, you can configure ORY Keto to +authorize before calling this endpoint with the following configuration: + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_CLIENT_ID`: The OAuth 2.0 Client ID that should be used to make the OAuth 2.0 + Introspection request. + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_CLIENT_SECRET`: The OAuth 2.0 Client Secret that should be used to make the OAuth 2.0 + Introspection request. + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_TOKEN_URL`: The URL of the OAuth 2.0 Token Endpoint (usually `/oauth2/token`). + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE`: If an OAuth 2.0 Scope is required to access the introspection URL, add that here. + You can define a list using the comma delimiter: `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE=scope-a,scope-b`. + +### OAuth 2.0 Client Credentials Authenticator + +This authenticator is located at `/warden/oauth2/clients/authorize` and is able to authenticate OAuth 2.0 Clients +using the OAuth 2.0 Client Credentials grant. A request payload looks as follows: + +``` +{ + "client_id": "client-id", + "client_secret": "client-secret", + "scope": ["scope-a", "scope-b"], + + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +If the OAuth 2.0 Token endpoint returns an access token, the OAuth 2.0 Clients is considered authenticated. The OAuth 2.0 +Client ID is used as subject when querying for ACPs: + +``` +{ + "subject": "client-id", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +#### Configuration + +This endpoint needs only one environment variable: + +* `AUTHENTICATOR_OAUTH2_CLIENT_CREDENTIALS_TOKEN_URL`: The URL of the OAuth 2.0 Token Endpoint (usually `/oauth2/token`). \ No newline at end of file diff --git a/docs/keto/1-overview/3-roles.md b/docs/keto/1-overview/3-roles.md new file mode 100644 index 000000000..eca89e7a5 --- /dev/null +++ b/docs/keto/1-overview/3-roles.md @@ -0,0 +1,74 @@ +--- +id: keto-overview-3-roles +title: Roles +--- + +ORY Keto supports the concept of roles (like in RBAC). This feature allows you to group together a number of subjects +under the same role. Whenever making a request to the Warden API, it will check the role's of a subject (if there are any) +and use them when looking up Access Control Policies. + +Assuming the following policies: + +```json +{ + "subjects": ["bob"], + "resources": ["blog_posts:my-first-blog-post"], + "actions": ["create"] + "effect": "allow" +} +``` + +```json +{ + "subjects": ["admin"], + "resources": ["blog_posts:my-first-blog-post"], + "actions": ["delete"] + "effect": "allow" +} +``` + +As you can see, `bob` is allowed to create resource `blog_posts:my-first-blog-post` and `admin` is allowed to delete it. +Warden request + +``` +{ + "subject": "bob", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +will return `{ "allowed": false }` while Warden request + +``` +{ + "subject": "admin", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +will return `{ "allowed": true }`. If we add `bob` to role `admin` using the [Role API](https://www.ory.sh/docs/api/keto) +or the CLI: + +``` +$ keto roles create admin \ + --endpoint http://your-keto-server/ + +$ keto roles members add admin bob \ + --endpoint http://your-keto-server/ +``` + +and redo the same Warden request + +``` +{ + "subject": "bob", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +the response will return `{ "allowed": true }` as well. + +Currently, we do not support hierarchy in roles but we might add that at a later stage. diff --git a/docs/keto/1-overview/4-best-practices.md b/docs/keto/1-overview/4-best-practices.md new file mode 100644 index 000000000..a9d73cd4f --- /dev/null +++ b/docs/keto/1-overview/4-best-practices.md @@ -0,0 +1,53 @@ +--- +id: keto-overview-4-best-practices +title: Best Practices +--- + +This sections gives an overview of best practices for access control policies +we developed over the years at ORY. + + + +## Scalability + +Access Control Policies not using any regular expressions are quite scalable. Defining many regular expressions and +a lot of policies (50.000+) may have a notable performance impact on CPU, your database and generally increase response +times. This is because regular expressions can not be indexed. Regular expressions have a complexity of `O(n)` in Go, +but that is still getting slow when you define too many. + +Try to solve your access control definitions with a few generalized policies, and try to leverage Warden Groups. + +## URNs + +> “There are only two hard things in Computer Science: cache invalidation and naming things.” +-- Phil Karlton + +URN naming is as hard as naming API endpoints. Thankfully, by doing the latter, the former is usually solved as well. +We will explore further best practices in the following sections. + +## Scope the Organization Name + +A rule of thumb is to prefix resource names with a domain that represents the organization creating the software. + +* **Do not:** `` +* **Do:** `:` + +## Scope Actions, Resources and Subjects + +It is wise to scope actions, resources, and subjects in order to prevent name collisions: + +* **Do not:** `myorg.com:`, `myorg.com:`, `myorg.com:` +* **Do:** `myorg.com:subjects:`, `myorg.com:resources:`, `myorg.com:actions:` +* **Do:** `subjects:myorg.com:`, `resources:myorg.com:`, `actions:myorg.com:` + +## Multi-Tenant Systems + +Multi-tenant systems typically have resources which should not be access by other tenants in the system. This can be +achieved by adding the tenant id to the URN: + +* **Do:** `resources:myorg.com:tenants::` + +In some environments, it is common to have organizations and projects belonging to those organizations. Here, the +following URN semantics can be used: + +* **Do:** `resources:myorg.com:organizations::projects::` diff --git a/docs/keto/1-overview/5-conditions.md b/docs/keto/1-overview/5-conditions.md new file mode 100644 index 000000000..2bad01ec9 --- /dev/null +++ b/docs/keto/1-overview/5-conditions.md @@ -0,0 +1,304 @@ +--- +id: keto-overview-5-conditions +title: Conditions & Context +--- + + + +## Overview + +Conditions are defined in policies. Contexts are defined in access control requests. Conditions use contexts and decide +if a policy is responsible for handling the access request at hand. + +Conditions are functions returning true or false given a context. Because conditions implement logic, +they must be programmed. ORY Keto uses conditions defined in [ORY Ladon](https://github.com/ory/ladon/#conditions). +Adding new condition handlers must be done through creating a pull request in the ORY Ladon repository. + +A condition has always the same JSON format: + +```json +{ + "subjects": ["..."], + "actions" : ["..."], + "effect": "allow", + "resources": ["..."], + "conditions": { + "this-key-will-be-matched-with-the-context": { + "type": "SomeConditionType", + "options": { + "some": "configuration options set by the condition type" + } + } + } +} +``` + +The context in the access request made to ORY Keto's Warden API must match the specified key in the condition +in order to be evaluated by the condition logic: + +```json +{ + "subject": "...", + "action" : "...", + "resource": "...", + "context": { + "this-key-will-be-matched-with-the-context": { "foo": "bar" } + } +} +``` + +### CIDR Condition + +The CIDR condition matches CIDR IP Ranges. An exemplary policy definition could look as follows. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "remoteIPAddress": { + "type": "CIDRCondition", + "options": { + "cidr": "192.168.0.0/16" + } + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "remoteIPAddress": "192.168.0.5" + } +} +``` + +The next access request would be denied as the condition is not fulfilled and thus no policy is matched. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "remoteIPAddress": "255.255.0.0" + } +} +``` + +The next access request would also be denied as the context is not using the key `remoteIPAddress` but instead `someOtherKey`. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someOtherKey": "192.168.0.5" + } +} +``` + +### String Equal Condition + +Checks if the value passed in the access request's context is identical with the string that was given initially. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "someKeyName": { + "type": "StringEqualCondition", + "options": { + "equals": "the-value-should-be-this" + } + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "the-value-should-be-this" + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "this-is-a-different-value" + } +} +``` + +### String Match Condition + +Checks if the value passed in the access request's context matches the regular expression that was given initially. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "someKeyName": { + "type": "StringMatchCondition", + "options": { + "equals": "regex-pattern-here.+" + } + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "regex-pattern-here-matches" + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "regex-pattern-here" + } +} +``` + +### Subject Condition + +Checks if the access request's subject is identical with the string specified in the context. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "owner": { + "type": "EqualsSubjectCondition", + "options": {} + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "owner": "users:maria" + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "owner": "another-user" + } +} +``` + +This condition makes more sense when being used with access tokens where the subject is extracted from the token. + +### String Pairs Equal Condition + +Checks if the value passed in the access request's context contains two-element arrays and that both elements in each pair are equal. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "someKey": { + "type": "StringPairsEqualCondition", + "options": {} + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKey": [ + ["some-arbitrary-pair-value", "some-arbitrary-pair-value"], + ["some-other-arbitrary-pair-value", "some-other-arbitrary-pair-value"] + ] + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKey": [ + ["some-arbitrary-pair-value", "some-other-arbitrary-pair-value"] + ] + } +} +``` diff --git a/docs/keto/1-overview/6-installation.md b/docs/keto/1-overview/6-installation.md new file mode 100644 index 000000000..d6a851c8d --- /dev/null +++ b/docs/keto/1-overview/6-installation.md @@ -0,0 +1,35 @@ +--- +id: keto-overview-6-installation +title: Installing ORY Keto +--- + + + +You can install ORY Keto by downloading the [binaries](https://github.com/ory/keto/releases), by using +the precompiled Docker Image available at [Docker Hub](https://hub.docker.com/r/oryd/keto/), or by +compiling the code yourself. + +## Docker Hub + +The recommended way to install and run ORY Keto is via docker: + +```sh +$ docker run oryd/keto: help +``` + +## Binaries + +If you download the binaries, make sure to add them to your path (e.g. `/usr/bin`). Then, run `keto help` + +## From Source + +To install ORY Keto from source, you need to have Go 1.10+ installed as well as [go/dep](https://golang.github.io/dep/). +Then, run: + +``` +$ go get -d -u github.com/ory/keto +$ cd $GOPATH/src/github.com/ory/keto +$ dep ensure -vendor-only +$ go install . +$ keto help +``` diff --git a/docs/keto/1-overview/7-configuration.md b/docs/keto/1-overview/7-configuration.md new file mode 100644 index 000000000..0261bc180 --- /dev/null +++ b/docs/keto/1-overview/7-configuration.md @@ -0,0 +1,138 @@ +--- +id: keto-overview-7-configuration +title: Configuring and Running ORY Keto +--- +# + +ORY Keto supports two types of storage adapters: + +* In-memory: This adapter does not work with more than one instance ("cluster") and any state is lost after restarting the instance. +* SQL: This adapter works with more than one instance and state is not lost after restarts. + +The SQL adapter supports two DBMS: PostgreSQL 9.6+ and MySQL 5.7+. Please note that +older MySQL versions have issues with the database schema. +For more information [go here](https://github.com/ory/hydra/issues/377). + +ORY Keto supports various authentication strategies. Depending on what strategies +you want to use, you will have to configure more services (e.g. ORY Hydra). In this tutorial, we will +set up ORY Keto without any of the other services. Please refer to the [warden chapter](./2-warden.md) +to see how to configure each authentication strategy. + +This guide will: + +1. Download and run a PostgreSQL container in Docker. +2. Download and run ORY Keto using Docker. + +## Create a Network + +Before we can start, a network must be created which we will attach all our Docker containers to. That way, the containers +can talk to one another. + +``` +$ docker network create ketoguide +``` + +## Start the PostgreSQL Container + +For the purpose of this tutorial, we will use PostgreSQL as a database. As you probably already know, don't run databases in Docker in production! +For the sake of this tutorial however, let's use Docker to quickly deploy the database. + +``` +$ docker run \ + --network ketoguide \ + --name ory-keto-example--postgres \ + -e POSTGRES_USER=keto \ + -e POSTGRES_PASSWORD=secret \ + -e POSTGRES_DB=keto \ + -d postgres:9.6 +``` + +This command wil start a postgres instance with name `ory-keto-example--postgres`, set up a database called `keto` +and create a user `keto` with password `secret`. + +## Run the ORY Keto Service + +``` +# The database url points us at the postgres instance. This could also be an ephermal in-memory database (`export DATABASE_URL=memory`) +# or a MySQL URI. +$ export DATABASE_URL=postgres://keto:secret@ory-keto-example--postgres:5432/keto?sslmode=disable + +# This pulls the latest image from Docker Hub +$ docker pull oryd/keto:v0.1.5-sandbox_oryOS.5 + +# ORY Keto does not do magic, it requires conscious decisions, for example running SQL migrations which is required +# when installing a new version of ORY Keto, or upgrading an existing installation. +# It is the equivalent to `keto migrate sql postgres://keto:secret@ory-keto-example--postgres:5432/keto?sslmode=disable` +$ docker run -it --rm \ + --network ketoguide \ + oryd/keto:v0.1.5-sandbox_oryOS.5 \ + migrate sql $DATABASE_URL + +Applying `client` SQL migrations... +[...] +Migration successful! + +# Next, let's run the server! +$ docker run -d \ + --name ory-keto-example--keto \ + --network ketoguide \ + -p 4466:4466 \ + -e DATABASE_URL=$DATABASE_URL \ + oryd/keto:v0.1.5-sandbox_oryOS.5 \ + serve +``` + +Great, the server running now! Make sure to check the logs and see if there were +any errors or issues before going to the next steps: + +``` +$ docker logs ory-keto-example--keto +``` + +## Running CLI Commands + +You can now create your first policy: + +``` +$ docker run -it --rm \ + --network ketoguide \ + oryd/keto:v0.1.5-sandbox_oryOS.5 \ + policies create --endpoint http://ory-keto-example--keto:4466/ \ + --id example-policy \ + --allow \ + -a delete \ + -s alice \ + -r "blog_posts:my-first-blog-post" +``` + +List all existing policies: + +``` +$ docker run -it --rm \ + --network ketoguide \ + oryd/keto:v0.1.5-sandbox_oryOS.5 \ + --endpoint http://ory-keto-example--keto:4466/ \ + policies get example-policy +``` + +And make some Warden requests: + +``` +$ docker run -it --rm \ + --network ketoguide \ + oryd/keto:v0.1.5-sandbox_oryOS.5 \ + warden authorize subject --endpoint http://ory-keto-example--keto:4466/ \ + --action delete \ + --subject alice \ + --resource "blog_posts:my-first-blog-post" +``` + +## Securing ORY Keto + +Similar to other services in our ecosystem, ORY Keto has no native access control. This means that any request +made to e.g. `/policies` or `/warden/...` is considered authenticated and thus executed. However, these endpoints +are very sensitive as they define who is allowed to do what in your system. + +Please use an API Gateway or a similar mechanism to protect these endpoints. How you protect them, is up to you. + +If you require dedicated help with this, consider asking us for [consultancy](mailto:hi@ory.sh). diff --git a/docs/keto/index.md b/docs/keto/index.md new file mode 100644 index 000000000..c4636c899 --- /dev/null +++ b/docs/keto/index.md @@ -0,0 +1,20 @@ +--- +id: keto-index +title: Introduction +--- + +Welcome to the ORY Keto documentation! + +ORY Keto is a service that stores permissions and can answer if an identity is allowed to perform a certain action over +REST. If you came here to answer + +* if a certain user is allowed to modify e.g. a blog article +* if a robot is allowed to e.g. print a document + +you have come to the right place. If you came here, because your permission system involves + +* complex multi-tenant environments +* permissions based on ownership, ip address, time of day +* or other advanced uses + +you are the right place even more so. \ No newline at end of file diff --git a/docs/keto/next/configure-deploy.md b/docs/keto/next/configure-deploy.md new file mode 100644 index 000000000..d42d034c4 --- /dev/null +++ b/docs/keto/next/configure-deploy.md @@ -0,0 +1,156 @@ +--- +id: keto-next-configure-deploy +title: Configure and Deploy +--- + +As all other ORY services, ORY Keto is implemented according to 12factor principles and completely stateless. To store +state, ORY Keto supports two types of storage adapters: + +* In-memory: This adapter does not work with more than one instance ("cluster") and any state is lost after restarting the instance. +* SQL: This adapter works with more than one instance and state is not lost after restarts. + +The SQL adapter supports two DBMS: PostgreSQL 9.6+ and MySQL 5.7+. Please note that +older MySQL versions may have issues with the database schema. We recommend working with PostgreSQL as migrations will be +faster. + +This guide will: + +1. Download and run a PostgreSQL container in Docker. +2. Download and run ORY Keto using Docker. + +## Create a Network + +Before we can start, a network must be created which we will attach all our Docker containers to. That way, the containers +can talk to one another. + +``` +$ docker network create ketoguide +``` + +## Start the PostgreSQL Container + +For the purpose of this tutorial, we will use PostgreSQL as a database. As you probably already know, don't run databases in Docker in production! +For the sake of this tutorial however, let's use Docker to quickly deploy the database. + +``` +$ docker run \ + --network ketoguide \ + --name ory-keto-example--postgres \ + -e POSTGRES_USER=keto \ + -e POSTGRES_PASSWORD=secret \ + -e POSTGRES_DB=keto \ + -d postgres:9.6 +``` + +This command wil start a postgres instance with name `ory-keto-example--postgres`, set up a database called `keto` +and create a user `keto` with password `secret`. + +## Run the ORY Keto Service + +``` +# The database url points us at the postgres instance. This could also be an ephermal in-memory database (`export DATABASE_URL=memory`) +# or a MySQL URI. +$ export DATABASE_URL=postgres://keto:secret@ory-keto-example--postgres:5432/keto?sslmode=disable + +# This pulls the latest image from Docker Hub +$ docker pull oryd/keto:unstable + +# ORY Keto does not do magic, it requires conscious decisions, for example running SQL migrations which is required +# when installing a new version of ORY Keto, or upgrading an existing installation. +# It is the equivalent to `DATABASE_URL=postgres://keto:secret@ory-keto-example--postgres:5432/keto?sslmode=disable keto migrate sql` +$ docker run -it --rm \ + --network ketoguide \ + -e DATABASE_URL=$DATABASE_URL \ + oryd/keto:unstable \ + migrate sql -e + +Applying `client` SQL migrations... +[...] +Migration successful! + +# Next, let's run the server! +$ docker run -d \ + --name ory-keto-example--keto \ + --network ketoguide \ + -p 4466:4466 \ + -e DATABASE_URL=$DATABASE_URL \ + oryd/keto:unstable \ + serve +``` + +Great, the server running now! Make sure to check the logs and see if there were +any errors or issues before going to the next steps: + +``` +$ docker logs ory-keto-example--keto +``` + +You should see one line showing where the server is running: + +``` +time="2018-10-27T11:48:56Z" level=info msg="Listening on http://localhost:4466" +``` + +## Working with the CLI + +Let's examine how we can work with the CLI to manage ORY Keto. We will use the ORY Access Control Policy Engine (`/engines/acp/ory`) +with the `exact` matcher and define policies and check if certain users are allowed to do certain things. Let's create +the first policy: + +``` +$ mkdir policies + +$ cat > policies/example-policy.json <"] +} +``` + +In the example above, the (incomplete) policy would match every subject that is prefixed with `users:`, so for example +`users:alice`, `users:bob`. In ORY Ladon / ORY Keto, regular expressions are delimited with `<` and `>`. +For example, `"users:.*"` is not a valid regular expression, just a simple string. + +The next example will allow all subjects with prefix `user:` to read (`actions:read`) all resources that match `resources:blog_posts:<[0-9]+>` +(e.g. `resources:blog_posts:1234` but not `resources:blog_posts:abcde`): + +```json +{ + "subjects": ["users:<.*>"], + "resources": ["resources:blog_posts:<[0-9]+>"], + "actions": ["actions:read"] + "effect": "allow" +} +``` + +### Computational Overhead + +Different pattern matching strategies have different computational complexity, considering performance when choosing +an approach is important: + +- Case Sensitive Equality: No computational overhead. +- URN: Little computational overhead. +- Regex: Considerable computational overhead. + +## Conditions + +Conditions are another powerful concept. So far, we covered that an ORY ACP applies to a list of +`subjects`, `resources`, and `actions`. Conditions narrow down the use cases in which a certain ACP applies. A condition +may, for example, mandate that the IP Address of the client making the request has to match e.g. `192.168.0.0/16` or that +the subject is also the owner of the resource: + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "remoteIPAddress": { + "type": "CIDRCondition", + "options": { + "cidr": "192.168.0.0/16" + } + } + } +} +``` + +Conditions are defined in policies. Context is defined when asking if someone is allowed to do something (access control). Conditions use contexts and decide +if a policy is responsible for handling the access request at hand. + +Conditions are functions returning true or false given a context. Because conditions implement logic, +they must be programmed. ORY Keto provides several conditions out of the box, which can be improved or extended at any time. + +A condition has always the same JSON format: + +```json +{ + "subjects": ["..."], + "actions" : ["..."], + "effect": "allow", + "resources": ["..."], + "conditions": { + "this-key-will-be-matched-with-the-context": { + "type": "SomeConditionType", + "options": { + "some": "configuration options set by the condition type" + } + } + } +} +``` + +The context in the access request made to the ORY ACP Allowed API must match the specified key in the condition +in order to be evaluated by the condition logic: + +```json +{ + "subject": "...", + "action" : "...", + "resource": "...", + "context": { + "this-key-will-be-matched-with-the-context": { "foo": "bar" } + } +} +``` + +### CIDR Condition + +The CIDR condition matches CIDR IP Ranges. An exemplary policy definition could look as follows. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "remoteIPAddress": { + "type": "CIDRCondition", + "options": { + "cidr": "192.168.0.0/16" + } + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "remoteIPAddress": "192.168.0.5" + } +} +``` + +The next access request would be denied as the condition is not fulfilled and thus no policy is matched. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "remoteIPAddress": "255.255.0.0" + } +} +``` + +The next access request would also be denied as the context is not using the key `remoteIPAddress` but instead `someOtherKey`. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someOtherKey": "192.168.0.5" + } +} +``` + +##### String Equal Condition + +Checks if the value passed in the access request's context is identical with the string that was given initially. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "someKeyName": { + "type": "StringEqualCondition", + "options": { + "equals": "the-value-should-be-this" + } + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "the-value-should-be-this" + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "this-is-a-different-value" + } +} +``` + +### String Match Condition + +Checks if the value passed in the access request's context matches the regular expression that was given initially. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "someKeyName": { + "type": "StringMatchCondition", + "options": { + "equals": "regex-pattern-here.+" + } + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "regex-pattern-here-matches" + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKeyName": "regex-pattern-here" + } +} +``` + +### Subject Condition + +Checks if the access request's subject is identical with the string specified in the context. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "owner": { + "type": "EqualsSubjectCondition", + "options": {} + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "owner": "users:maria" + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "owner": "another-user" + } +} +``` + +This condition makes more sense when being used with access tokens where the subject is extracted from the token. + +### String Pairs Equal Condition + +Checks if the value passed in the access request's context contains two-element arrays and that both elements in each pair are equal. + +```json +{ + "description": "One policy to rule them all.", + "subjects": ["users:maria"], + "actions" : ["delete", "create", "update"], + "effect": "allow", + "resources": ["resources:articles:<.*>"], + "conditions": { + "someKey": { + "type": "StringPairsEqualCondition", + "options": {} + } + } +} +``` + +The following access request would be allowed. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKey": [ + ["some-arbitrary-pair-value", "some-arbitrary-pair-value"], + ["some-other-arbitrary-pair-value", "some-other-arbitrary-pair-value"] + ] + } +} +``` + +The following access request would be denied. + +```json +{ + "subject": "users:maria", + "action" : "delete", + "resource": "resources:articles:12345", + "context": { + "someKey": [ + ["some-arbitrary-pair-value", "some-other-arbitrary-pair-value"] + ] + } +} +``` + +## Roles + +ORY ACPs support a role concept, similar to RBAC. This feature allows you to group together a number of subjects +under the same role. Whenever making a request to the Allowed API, it will check the roles of a subject (if there are any) +and use them when looking up the `subjects` field. + +Assuming the following policies: + +```json +{ + "subjects": ["bob"], + "resources": ["blog_posts:my-first-blog-post"], + "actions": ["create"] + "effect": "allow" +} +``` + +```json +{ + "subjects": ["admin"], + "resources": ["blog_posts:my-first-blog-post"], + "actions": ["delete"] + "effect": "allow" +} +``` + +As you can see, `bob` is allowed to create resource `blog_posts:my-first-blog-post` and `admin` is allowed to delete it. +Making the following request to the Allowed API + +``` +{ + "subject": "bob", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +will return `{ "allowed": false }` while this request + +``` +{ + "subject": "admin", + "action" : "delete", + "resource": "blog_posts:my-first-blog-post" +} +``` + +will return `{ "allowed": false }`. + +## Implementation Status + +ORY Access Control Policies (regex, equality) are first-class citizens. We are working on adding +the urn strategy in the close future. + +## Best Practices + +This sections gives an overview of best practices for access control policies +we developed over the years at ORY. + +### URNs + +> “There are only two hard things in Computer Science: cache invalidation and naming things.” +-- Phil Karlton + +URN naming is as hard as naming API endpoints. Thankfully, by doing the latter, the former is usually solved as well. +We will explore further best practices in the following sections. + +### Scope the Organization Name + +A rule of thumb is to prefix resource names with a domain that represents the organization creating the software. + +* **Do not:** `` +* **Do:** `:` + +### Scope Actions, Resources and Subjects + +It is wise to scope actions, resources, and subjects in order to prevent name collisions: + +* **Do not:** `myorg.com:`, `myorg.com:`, `myorg.com:` +* **Do:** `myorg.com:subjects:`, `myorg.com:resources:`, `myorg.com:actions:` +* **Do:** `subjects:myorg.com:`, `resources:myorg.com:`, `actions:myorg.com:` + +### Multi-Tenant Systems + +Multi-tenant systems typically have resources which should not be access by other tenants in the system. This can be +achieved by adding the tenant id to the URN: + +* **Do:** `resources:myorg.com:tenants::` + +In some environments, it is common to have organizations and projects belonging to those organizations. Here, the +following URN semantics can be used: + +* **Do:** `resources:myorg.com:organizations::projects::` diff --git a/docs/keto/next/engines/index.md b/docs/keto/next/engines/index.md new file mode 100644 index 000000000..c88b3b9d3 --- /dev/null +++ b/docs/keto/next/engines/index.md @@ -0,0 +1,19 @@ +--- +id: keto-next-engines-index +title: Introduction +--- + +Whatever your system looks like, you probably have a concept of permissions which models who is allowed to do what ("access control"). +ORY Keto provides you with battle-tested, best practice access control concepts. + +This page will show the different concepts that have emerged as best practices and which are being used widely in software +today. + +While ORY Keto is in "sandbox" mode, not all access control mechanisms are supported. We will shine light on them anyways. + +Before we take a look at Access Control Policies in detail, let's get some of the basics figured out. Every app that +has users usually assigns permissions to these users ("Bob, Alice are allowed to write blog posts"). There are +various established practices for assigning one or more permissions to one or more users. + +In the context of access control, you'll often encounter **users**, **identities** or **subjects**. +They usually include users, robots, cronjobs, services, ... . diff --git a/docs/keto/next/engines/rbac.md b/docs/keto/next/engines/rbac.md new file mode 100644 index 000000000..71b8224f5 --- /dev/null +++ b/docs/keto/next/engines/rbac.md @@ -0,0 +1,46 @@ +--- +id: keto-next-engines-rbac +title: Role Based Access Control (RBAC) +--- + +[Role Based Access Control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) maps subjects to roles +and roles to permissions. The goal of (H)RBAC is to make permission management convenient by grouping subjects +in roles and assigning permissions roles. This type of access control is very common in web applications, where you often +encounter roles such as "administrator", "moderator", and so on. + +What's common in RBAC is that roles can inherit permissions from one another. This concept is called **HRBAC or +Hierarchical Role Based Access Control**. The role administrator, for example, could inherit all permissions from role +moderator. This further decreases management complexity as, instead of adding all permissions to administrator or +assigning a user to both moderator and administrator roles, you simply point the administrator role to inherit +from the moderator one. + +Let's come back to alice, bob, peter, and blog posts and the matrix from the ACL example, but this time we define +roles "reader", "author", "admin" and model the ACL example using (H)RBAC: + +![(H)RBAC Example](../../../../images/docs/keto/rbac.png). + +As you can see, `admin` inherits from `author`, which inherits from `reader`. Only `alice` (or rather `admin`) can delete blog posts, +whereas `author` can create and modify blog posts. We assign the roles to our subjects `bob`, `peter`, `alice` and +express the same permissions as in the ACL example. + +(H)RBAC is everywhere. If you ever installed a forum software such as [phpBB](https://www.phpbb.com/support/docs/en/3.1/ug/adminguide/permissions_roles/), +[Wordpress](https://codex.wordpress.org/Roles_and_Capabilities) or others, you have definitely encountered ACL, (H)RBAC, or both. + +(H)RBAC reduces management complexity & overhead with large user/subject bases. Sometimes however, (H)RBAC is not enough as well. +That's the case when you're trying to express ownership (e.g. `bob` can modify blog posts, but only his own), or +have attributes (e.g. `bob` works in department `blog`), or multi-tenant environments. + +**Benefits:** +* Reduces management complexity where many identities share the same permissions. +* Makes management even easier with role hierarchy. +* Is well established and easily understood by many developers as it is a de-facto standard for web applications. + +**Shortcomings:** +* Has no concept of context: + * There is no concept of ownership: *Dan is the author of article "Hi World" and is thus allowed to update it*. + * There is no concept of environment: *Dan is allowed to access accounting services when the request comes from IP 10.0.0.3*. + * There is no concept of tenants: *Dan is allowed to access resources on the "dan's test" tenant*. + * ... + +**Implementation status:** (Hierarchical) Role Based Access Control is currently not implemented but will be first-class citizens in the future. +To bump this in priority, [click here](https://github.com/ory/keto/issues/60). diff --git a/docs/keto/next/index.md b/docs/keto/next/index.md new file mode 100644 index 000000000..9d4da0a43 --- /dev/null +++ b/docs/keto/next/index.md @@ -0,0 +1,26 @@ +--- +id: keto-next-index +title: Introduction +--- + +ORY Keto is an permission server that implements best practice access control mechanisms. If you came looking for +the answer to the question: + +* is certain user is allowed to modify that blog article? +* is this service is allowed to print a document? +* is the user of the ACME organisation allowed to modify data in one of their tenants? +* is this process allowed to execute the worker when coming from IP 10.0.0.2 between 4pm and 5pm on every monday? +* ... + +ORY Keto provides various access control engines: + +* Available today: + * ORY-flavored Access Control Policies with exact, glob, and regexp matching strategies +* Available soon: + * [Access Control Lists](https://en.wikipedia.org/wiki/Access_control_list) + * [Role Based Access Control](https://de.wikipedia.org/wiki/Role_Based_Access_Control) + * Role Based Access Control with Context (Google/Kubernetes-flavored) + * Amazon Web Services Identity & Access Management Policies (AWS IAM Policies) + +Each mechanism is powered by a decision engine implemented on top of the +[Open Policy Agent](https://www.openpolicyagent.org/) and provides well-defined management and authorization REST API endpoints. diff --git a/docs/keto/next/install.md b/docs/keto/next/install.md new file mode 100644 index 000000000..c4a534445 --- /dev/null +++ b/docs/keto/next/install.md @@ -0,0 +1,31 @@ +--- +id: keto-next-install +title: Install +--- + +You can install ORY Keto by downloading the [binaries](https://github.com/ory/keto/releases), by using +the precompiled Docker Image available at [Docker Hub](https://hub.docker.com/r/oryd/keto/), or by +compiling the code yourself. + +## Docker Hub + +The recommended way to install and run ORY Keto is via docker: + +```sh +$ docker run oryd/keto: help +``` + +## Binaries + +If you download the binaries, make sure to add them to your path (e.g. `/usr/bin`). Then, run `keto help` + +## From Source + +To install ORY Keto from source, you need to have Go 1.11+ installed, and run: + +``` +$ go get -d -u github.com/ory/keto +$ cd $(go env GOPATH)/src/github.com/ory/keto +$ make install-stable +$ $(go env GOPATH)/bin/keto help +``` diff --git a/docs/keto/next/sdk/api.md b/docs/keto/next/sdk/api.md new file mode 100644 index 000000000..06b4d8fe3 --- /dev/null +++ b/docs/keto/next/sdk/api.md @@ -0,0 +1,5087 @@ +--- +title: REST API +id: keto-next-sdk-api +--- + + + +Package main ORY Keto + +> You are viewing a REST API documentation. This documentation is auto-generated from a swagger specification which +itself is generated from annotations in the source files of the project. It is possible that this documentation includes +bugs and that code samples are incomplete or wrong. +> +> If you find issues in the respective documentation, please do not edit the +markdown files directly (as they are generated) but raise an issue on the project's GitHub instead. This documentation +will improve over time with your help! If you have ideas how to improve this part of the documentation, feel free to +share them in a [GitHub issue](https://github.com/ory/docs/issues/new) any time. + + +## health + + + +### Check the Alive Status + +``` +GET /health/alive HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns a 200 status code when the HTTP server is up running. +This status does currently not include checks whether the database connection is working. +This endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set. + +Be aware that if you are running multiple nodes of ORY Keto, the health status will never refer to the cluster state, only to a single instance. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|healthStatus|[healthStatus](#schemahealthstatus)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /health/alive \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/health/alive", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/health/alive', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/health/alive"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/health/alive', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/health/alive', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Check the Readiness Status + +``` +GET /health/ready HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g. +the database) are responsive as well. + +This status does currently not include checks whether the database connection is working. +This endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set. + +Be aware that if you are running multiple nodes of ORY Keto, the health status will never refer to the cluster state, only to a single instance. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|healthStatus|[healthStatus](#schemahealthstatus)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|healthNotReadyStatus|[healthNotReadyStatus](#schemahealthnotreadystatus)| + +##### Examples + +###### 200 response + +```json +{ + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /health/ready \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/health/ready", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/health/ready', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/health/ready"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/health/ready', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/health/ready', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## policy + + + +### listPolicies + +``` +GET /policies HTTP/1.1 +Accept: application/json + +``` + +List Access Control Policies + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|offset|query|integer(int64)|false|The offset from where to start looking.| +|limit|query|integer(int64)|false|The maximum amount of policies returned.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A policy|Inline| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[[policy](#schemapolicy)]|false|none|none| +|» actions|[string]|false|none|Actions impacted by the policy.| +|» conditions|object|false|none|Conditions under which the policy is active.| +|»» **additionalProperties**|object|false|none|none| +|»»» options|object|false|none|none| +|»»»» **additionalProperties**|object|false|none|none| +|»»» type|string|false|none|none| +|»» description|string|false|none|Description of the policy.| +|»» effect|string|false|none|Effect of the policy| +|»» id|string|false|none|ID of the policy.| +|»» resources|[string]|false|none|Resources impacted by the policy.| +|»» subjects|[string]|false|none|Subjects impacted by the policy.| + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +[ + { + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] + } +] +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /policies \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/policies", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/policies', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/policies"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/policies', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/policies', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### createPolicy + +``` +POST /policies HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Create an Access Control Policy + +#### Request body + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[policy](#schemapolicy)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|policy|[policy](#schemapolicy)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 201 response + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /policies \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/policies", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/policies', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/policies"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/policies', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/policies', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### getPolicy + +``` +GET /policies/{id} HTTP/1.1 +Accept: application/json + +``` + +Get an Access Control Policy + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the policy.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|policy|[policy](#schemapolicy)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /policies/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/policies/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/policies/{id}', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/policies/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/policies/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/policies/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### updatePolicy + +``` +PUT /policies/{id} HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Update an Access Control Policy + +#### Request body + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the policy.| +|body|body|[policy](#schemapolicy)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|policy|[policy](#schemapolicy)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /policies/{id} \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/policies/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/policies/{id}', { + method: 'PUT', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/policies/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.put( + '/policies/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.put '/policies/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### deletePolicy + +``` +DELETE /policies/{id} HTTP/1.1 +Accept: application/json + +``` + +Delete an Access Control Policy + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the policy.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /policies/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/policies/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/policies/{id}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/policies/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/policies/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/policies/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## role + + + +### List all roles + +``` +GET /roles HTTP/1.1 +Accept: application/json + +``` + +A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +This endpoint allows you to retrieve all roles that are stored in the system. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|member|query|string|false|The id of the member to look up.| +|limit|query|integer(int64)|false|The maximum amount of policies returned.| +|offset|query|integer(int64)|false|The offset from where to start looking.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A list of roles the member is belonging to|Inline| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[[role](#schemarole)]|false|none|[Role represents a group of users that share the same role. A role could be an administrator, a moderator, a regular user or some other sort of role.]| +|» id|string|false|none|ID is the role's unique id.| +|» members|[string]|false|none|Members is who belongs to the role.| + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +[ + { + "id": "string", + "members": [ + "string" + ] + } +] +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /roles \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/roles", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/roles', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/roles', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/roles', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Create a role + +``` +POST /roles HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +This endpoint allows you to create a new role. You may define members as well but you don't have to. + +#### Request body + +```json +{ + "id": "string", + "members": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[role](#schemarole)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|role|[role](#schemarole)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 201 response + +```json +{ + "id": "string", + "members": [ + "string" + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /roles \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/roles", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "id": "string", + "members": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/roles', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/roles', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/roles', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Get a role by its ID + +``` +DELETE /roles/{id} HTTP/1.1 +Accept: application/json + +``` + +A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +This endpoint allows you to delete an existing role. You have to know the role's ID. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the role to look up.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /roles/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/roles/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/roles/{id}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/roles/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/roles/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +``` +PUT /roles/{id} HTTP/1.1 +Accept: application/json + +``` + +This endpoint allows you to overwrite a role. You have to know the role's ID. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /roles/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/roles/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/roles/{id}', { + method: 'PUT', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.put( + '/roles/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.put '/roles/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Add members to a role + +``` +POST /roles/{id}/members HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +This endpoint allows you to add members (users, applications, ...) to a specific role. You have to know the role's ID. + +#### Request body + +```json +{ + "members": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the role to modify.| +|body|body|[roleMembers](#schemarolemembers)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /roles/{id}/members \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/roles/{id}/members", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "members": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/roles/{id}/members', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles/{id}/members"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/roles/{id}/members', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/roles/{id}/members', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Remove members from a role + +``` +DELETE /roles/{id}/members HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +This endpoint allows you to remove members (users, applications, ...) from a specific role. You have to know the role's ID. + +#### Request body + +```json +{ + "members": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the role to modify.| +|body|body|[roleMembers](#schemarolemembers)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /roles/{id}/members \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/roles/{id}/members", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "members": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/roles/{id}/members', { + method: 'DELETE', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles/{id}/members"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.delete( + '/roles/{id}/members', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.delete '/roles/{id}/members', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## version + + + +### Get the version of Keto + +``` +GET /version HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns the version as `{ "version": "VERSION" }`. The version is only correct with the prebuilt binary and not custom builds. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|version|[version](#schemaversion)| + +##### Examples + +###### 200 response + +```json +{ + "version": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /version \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/version", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/version', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/version"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/version', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/version', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## warden + + + +### Check if an OAuth 2.0 access token is authorized to access a resource + +``` +POST /warden/oauth2/access-tokens/authorize HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Checks if a token is valid and if the token subject is allowed to perform an action on a resource. +This endpoint requires a token, a scope, a resource name, an action name and a context. + +If a token is expired/invalid, has not been granted the requested scope or the subject is not allowed to +perform the action on the resource, this endpoint returns a 200 response with `{ "allowed": false }`. + +This endpoint passes all data from the upstream OAuth 2.0 token introspection endpoint. If you use ORY Hydra as an +upstream OAuth 2.0 provider, data set through the `accessTokenExtra` field in the consent flow will be included in this +response as well. + +#### Request body + +```json +{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ], + "token": "string" +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[wardenOAuth2AccessTokenAuthorizationRequest](#schemawardenoauth2accesstokenauthorizationrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|wardenOAuth2AccessTokenAuthorizationResponse|[wardenOAuth2AccessTokenAuthorizationResponse](#schemawardenoauth2accesstokenauthorizationresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "allowed": true, + "aud": [ + "string" + ], + "client_id": "string", + "exp": "2018-11-12T10:44:13Z", + "iat": "2018-11-12T10:44:13Z", + "iss": "string", + "nbf": "2018-11-12T10:44:13Z", + "scope": "string", + "session": { + "property1": {}, + "property2": {} + }, + "sub": "string", + "username": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /warden/oauth2/access-tokens/authorize \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/warden/oauth2/access-tokens/authorize", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ], + "token": "string" +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/warden/oauth2/access-tokens/authorize', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/warden/oauth2/access-tokens/authorize"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/warden/oauth2/access-tokens/authorize', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/warden/oauth2/access-tokens/authorize', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Check if an OAuth 2.0 Client is authorized to access a resource + +``` +POST /warden/oauth2/clients/authorize HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Checks if an OAuth 2.0 Client provided the correct access credentials and and if the client is allowed to perform +an action on a resource. This endpoint requires a client id, a client secret, a scope, a resource name, an action name and a context. + +#### Request body + +```json +{ + "action": "string", + "client_id": "string", + "client_secret": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[wardenOAuth2ClientAuthorizationRequest](#schemawardenoauth2clientauthorizationrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|wardenOAuth2ClientAuthorizationResponse|[wardenOAuth2ClientAuthorizationResponse](#schemawardenoauth2clientauthorizationresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "allowed": true, + "sub": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /warden/oauth2/clients/authorize \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/warden/oauth2/clients/authorize", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "action": "string", + "client_id": "string", + "client_secret": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/warden/oauth2/clients/authorize', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/warden/oauth2/clients/authorize"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/warden/oauth2/clients/authorize', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/warden/oauth2/clients/authorize', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Check if a subject is authorized to access a resource + +``` +POST /warden/subjects/authorize HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Checks if a subject (e.g. user ID, API key, ...) is allowed to perform a certain action on a resource. + +#### Request body + +```json +{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "subject": "string" +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[WardenSubjectAuthorizationRequest](#schemawardensubjectauthorizationrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|wardenSubjectAuthorizationResponse|[wardenSubjectAuthorizationResponse](#schemawardensubjectauthorizationresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "allowed": true, + "sub": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /warden/subjects/authorize \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/warden/subjects/authorize", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "subject": "string" +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/warden/subjects/authorize', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/warden/subjects/authorize"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/warden/subjects/authorize', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/warden/subjects/authorize', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ +## Schemas + +AuthenticationOAuth2ClientCredentialsRequest +#### AuthenticationOAuth2ClientCredentialsRequest + + + +```json +{ + "client_id": "string", + "client_secret": "string", + "scope": [ + "string" + ] +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|client_id|string|false|none|Token is the token to introspect.| +|client_secret|string|false|none|none| +|scope|[string]|false|none|Scope is an array of scopes that are required.| + +AuthenticationOAuth2IntrospectionRequest +#### AuthenticationOAuth2IntrospectionRequest + + + +```json +{ + "scope": [ + "string" + ], + "token": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|scope|[string]|false|none|Scope is an array of scopes that are required.| +|token|string|false|none|Token is the token to introspect.| + +Authenticator +#### Authenticator + + + +```json +{} + +``` + +#### Properties + +*None* + +Firewall +#### Firewall + + + +```json +{} + +``` + +*Firewall offers various validation strategies for access tokens.* + +#### Properties + +*None* + +Handler +#### Handler + + + +```json +{ + "H": {}, + "Manager": {} +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|H|[Writer](#schemawriter)|false|none|Writer is a helper to write arbitrary data to a ResponseWriter| +|Manager|[Manager](#schemamanager)|false|none|none| + +IntrospectionResponse +#### IntrospectionResponse + + + +```json +{ + "active": true, + "aud": [ + "string" + ], + "client_id": "string", + "exp": 0, + "ext": { + "property1": {}, + "property2": {} + }, + "iat": 0, + "iss": "string", + "nbf": 0, + "scope": "string", + "sub": "string", + "token_type": "string", + "username": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|active|boolean|false|none|none| +|aud|[string]|false|none|none| +|client_id|string|false|none|none| +|exp|integer(int64)|false|none|none| +|ext|object|false|none|Session represents arbitrary session data.| +|» **additionalProperties**|object|false|none|none| +|iat|integer(int64)|false|none|none| +|iss|string|false|none|none| +|nbf|integer(int64)|false|none|none| +|scope|string|false|none|none| +|sub|string|false|none|Here, it's sub| +|token_type|string|false|none|none| +|username|string|false|none|none| + +Manager +#### Manager + + + +```json +{} + +``` + +#### Properties + +*None* + +OAuth2ClientCredentialsAuthentication +#### OAuth2ClientCredentialsAuthentication + + + +```json +{} + +``` + +#### Properties + +*None* + +OAuth2IntrospectionAuthentication +#### OAuth2IntrospectionAuthentication + + + +```json +{} + +``` + +#### Properties + +*None* + +Session +#### Session + + + +```json +{ + "GetSubject": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|GetSubject|string|false|none|none| + +WardenSubjectAuthorizationRequest +#### WardenSubjectAuthorizationRequest + + + +```json +{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "subject": "string" +} + +``` + +*AccessRequest is the warden's request object.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|action|string|false|none|Action is the action that is requested on the resource.| +|context|object|false|none|Context is the request's environmental context.| +|» **additionalProperties**|object|false|none|none| +|resource|string|false|none|Resource is the resource that access is requested to.| +|subject|string|false|none|Subejct is the subject that is requesting access.| + +Writer +#### Writer + + + +```json +{} + +``` + +*Writer is a helper to write arbitrary data to a ResponseWriter* + +#### Properties + +*None* + +authenticationDefaultSession +#### authenticationDefaultSession + + + +```json +{ + "allowed": true, + "sub": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| + +authenticationOAuth2ClientCredentialsSession +#### authenticationOAuth2ClientCredentialsSession + + + +```json +{ + "allowed": true, + "sub": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| + +authenticationOAuth2Session +#### authenticationOAuth2Session + + + +```json +{ + "allowed": true, + "aud": [ + "string" + ], + "client_id": "string", + "exp": "2018-11-12T10:44:13Z", + "iat": "2018-11-12T10:44:13Z", + "iss": "string", + "nbf": "2018-11-12T10:44:13Z", + "scope": "string", + "session": { + "property1": {}, + "property2": {} + }, + "sub": "string", + "username": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|aud|[string]|false|none|none| +|client_id|string|false|none|ClientID is the id of the OAuth2 client that requested the token.| +|exp|string(date-time)|false|none|ExpiresAt is the expiry timestamp.| +|iat|string(date-time)|false|none|IssuedAt is the token creation time stamp.| +|iss|string|false|none|Issuer is the id of the issuer, typically an hydra instance.| +|nbf|string(date-time)|false|none|none| +|scope|string|false|none|GrantedScopes is a list of scopes that the subject authorized when asked for consent.| +|session|object|false|none|Session represents arbitrary session data.| +|» **additionalProperties**|object|false|none|none| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| +|username|string|false|none|none| + +healthNotReadyStatus +#### healthNotReadyStatus + + + +```json +{ + "errors": { + "property1": "string", + "property2": "string" + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|errors|object|false|none|Errors contains a list of errors that caused the not ready status.| +|» **additionalProperties**|string|false|none|none| + +healthStatus +#### healthStatus + + + +```json +{ + "status": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|Status always contains "ok".| + +policy +#### policy + + + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|actions|[string]|false|none|Actions impacted by the policy.| +|conditions|object|false|none|Conditions under which the policy is active.| +|» **additionalProperties**|object|false|none|none| +|»» options|object|false|none|none| +|»»» **additionalProperties**|object|false|none|none| +|»» type|string|false|none|none| +|» description|string|false|none|Description of the policy.| +|» effect|string|false|none|Effect of the policy| +|» id|string|false|none|ID of the policy.| +|» resources|[string]|false|none|Resources impacted by the policy.| +|» subjects|[string]|false|none|Subjects impacted by the policy.| + +role +#### role + + + +```json +{ + "id": "string", + "members": [ + "string" + ] +} + +``` + +*Role represents a group of users that share the same role. A role could be an administrator, a moderator, a regular +user or some other sort of role.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|ID is the role's unique id.| +|members|[string]|false|none|Members is who belongs to the role.| + +roleMembers +#### roleMembers + + + +```json +{ + "members": [ + "string" + ] +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|members|[string]|false|none|none| + +swaggerCreatePolicyParameters +#### swaggerCreatePolicyParameters + + + +```json +{ + "Body": { + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[policy](#schemapolicy)|false|none|none| + +swaggerDoesWardenAllowAccessRequestParameters +#### swaggerDoesWardenAllowAccessRequestParameters + + + +```json +{ + "Body": { + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "subject": "string" + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[WardenSubjectAuthorizationRequest](#schemawardensubjectauthorizationrequest)|false|none|none| + +swaggerDoesWardenAllowClientRequestParameters +#### swaggerDoesWardenAllowClientRequestParameters + + + +```json +{ + "Body": { + "action": "string", + "client_id": "string", + "client_secret": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ] + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[wardenOAuth2ClientAuthorizationRequest](#schemawardenoauth2clientauthorizationrequest)|false|none|none| + +swaggerDoesWardenAllowTokenAccessRequestParameters +#### swaggerDoesWardenAllowTokenAccessRequestParameters + + + +```json +{ + "Body": { + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ], + "token": "string" + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[wardenOAuth2AccessTokenAuthorizationRequest](#schemawardenoauth2accesstokenauthorizationrequest)|false|none|none| + +swaggerGetPolicyParameters +#### swaggerGetPolicyParameters + + + +```json +{ + "id": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|The id of the policy. in: path| + +swaggerListPolicyParameters +#### swaggerListPolicyParameters + + + +```json +{ + "limit": 0, + "offset": 0 +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|limit|integer(int64)|false|none|The maximum amount of policies returned. in: query| +|offset|integer(int64)|false|none|The offset from where to start looking. in: query| + +swaggerListPolicyResponse +#### swaggerListPolicyResponse + + + +```json +{ + "Body": [ + { + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] + } + ] +} + +``` + +*A policy* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[[policy](#schemapolicy)]|false|none|in: body type: array| + +swaggerUpdatePolicyParameters +#### swaggerUpdatePolicyParameters + + + +```json +{ + "Body": { + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] + }, + "id": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[policy](#schemapolicy)|false|none|none| +|id|string|false|none|The id of the policy. in: path| + +swaggerWardenBaseRequest +#### swaggerWardenBaseRequest + + + +```json +{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string" +} + +``` + +*swager:model authorizedBaseRequest* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|action|string|false|none|Action is the action that is requested on the resource.| +|context|object|false|none|Context is the request's environmental context.| +|» **additionalProperties**|object|false|none|none| +|resource|string|false|none|Resource is the resource that access is requested to.| + +version +#### version + + + +```json +{ + "version": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|version|string|false|none|none| + +wardenOAuth2AccessTokenAuthorizationRequest +#### wardenOAuth2AccessTokenAuthorizationRequest + + + +```json +{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ], + "token": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|action|string|false|none|Action is the action that is requested on the resource.| +|context|object|false|none|Context is the request's environmental context.| +|» **additionalProperties**|object|false|none|none| +|resource|string|false|none|Resource is the resource that access is requested to.| +|scope|[string]|false|none|Scope is an array of scopes that are required.| +|token|string|false|none|Token is the token to introspect.| + +wardenOAuth2AccessTokenAuthorizationResponse +#### wardenOAuth2AccessTokenAuthorizationResponse + + + +```json +{ + "allowed": true, + "aud": [ + "string" + ], + "client_id": "string", + "exp": "2018-11-12T10:44:13Z", + "iat": "2018-11-12T10:44:13Z", + "iss": "string", + "nbf": "2018-11-12T10:44:13Z", + "scope": "string", + "session": { + "property1": {}, + "property2": {} + }, + "sub": "string", + "username": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|aud|[string]|false|none|none| +|client_id|string|false|none|ClientID is the id of the OAuth2 client that requested the token.| +|exp|string(date-time)|false|none|ExpiresAt is the expiry timestamp.| +|iat|string(date-time)|false|none|IssuedAt is the token creation time stamp.| +|iss|string|false|none|Issuer is the id of the issuer, typically an hydra instance.| +|nbf|string(date-time)|false|none|none| +|scope|string|false|none|GrantedScopes is a list of scopes that the subject authorized when asked for consent.| +|session|object|false|none|Session represents arbitrary session data.| +|» **additionalProperties**|object|false|none|none| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| +|username|string|false|none|none| + +wardenOAuth2ClientAuthorizationRequest +#### wardenOAuth2ClientAuthorizationRequest + + + +```json +{ + "action": "string", + "client_id": "string", + "client_secret": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ] +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|action|string|false|none|Action is the action that is requested on the resource.| +|client_id|string|false|none|Token is the token to introspect.| +|client_secret|string|false|none|none| +|context|object|false|none|Context is the request's environmental context.| +|» **additionalProperties**|object|false|none|none| +|resource|string|false|none|Resource is the resource that access is requested to.| +|scope|[string]|false|none|Scope is an array of scopes that are required.| + +wardenOAuth2ClientAuthorizationResponse +#### wardenOAuth2ClientAuthorizationResponse + + + +```json +{ + "allowed": true, + "sub": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| + +wardenSubjectAuthorizationResponse +#### wardenSubjectAuthorizationResponse + + + +```json +{ + "allowed": true, + "sub": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| + diff --git a/docs/keto/next/secure.md b/docs/keto/next/secure.md new file mode 100644 index 000000000..e9e9a06a4 --- /dev/null +++ b/docs/keto/next/secure.md @@ -0,0 +1,13 @@ +--- +id: keto-next-secure +title: Secure +--- + +Similar to other services in our ecosystem, ORY Keto has no integrated access control in it's APIs. This means that any request +made to any API endpoint is considered authenticated, authorized and is thus being executed. However, these endpoints +are very sensitive as they define who is allowed to do what in your system. + +Please use [ORY Oathkeeper](https://github.com/ory/oathkeeper), an API Gateway, or a similar mechanism to protect +these endpoints. How you protect them, is up to you. + +If you require dedicated help with this, consider [asking us](mailto:hi@ory.sh). diff --git a/docs/keto/sdk/api.md b/docs/keto/sdk/api.md new file mode 100644 index 000000000..dc15a32c7 --- /dev/null +++ b/docs/keto/sdk/api.md @@ -0,0 +1,5087 @@ +--- +title: REST API +id: keto-sdk-api +--- + + + +Package main ORY Keto + +> You are viewing a REST API documentation. This documentation is auto-generated from a swagger specification which +itself is generated from annotations in the source files of the project. It is possible that this documentation includes +bugs and that code samples are incomplete or wrong. +> +> If you find issues in the respective documentation, please do not edit the +markdown files directly (as they are generated) but raise an issue on the project's GitHub instead. This documentation +will improve over time with your help! If you have ideas how to improve this part of the documentation, feel free to +share them in a [GitHub issue](https://github.com/ory/docs/issues/new) any time. + + +## health + + + +### Check the Alive Status + +``` +GET /health/alive HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns a 200 status code when the HTTP server is up running. +This status does currently not include checks whether the database connection is working. +This endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set. + +Be aware that if you are running multiple nodes of ORY Keto, the health status will never refer to the cluster state, only to a single instance. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|healthStatus|[healthStatus](#schemahealthstatus)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /health/alive \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/health/alive", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/health/alive', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/health/alive"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/health/alive', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/health/alive', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Check the Readiness Status + +``` +GET /health/ready HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g. +the database) are responsive as well. + +This status does currently not include checks whether the database connection is working. +This endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set. + +Be aware that if you are running multiple nodes of ORY Keto, the health status will never refer to the cluster state, only to a single instance. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|healthStatus|[healthStatus](#schemahealthstatus)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|healthNotReadyStatus|[healthNotReadyStatus](#schemahealthnotreadystatus)| + +##### Examples + +###### 200 response + +```json +{ + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /health/ready \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/health/ready", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/health/ready', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/health/ready"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/health/ready', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/health/ready', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## policy + + + +### listPolicies + +``` +GET /policies HTTP/1.1 +Accept: application/json + +``` + +List Access Control Policies + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|offset|query|integer(int64)|false|The offset from where to start looking.| +|limit|query|integer(int64)|false|The maximum amount of policies returned.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A policy|Inline| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[[policy](#schemapolicy)]|false|none|none| +|» actions|[string]|false|none|Actions impacted by the policy.| +|» conditions|object|false|none|Conditions under which the policy is active.| +|»» **additionalProperties**|object|false|none|none| +|»»» options|object|false|none|none| +|»»»» **additionalProperties**|object|false|none|none| +|»»» type|string|false|none|none| +|»» description|string|false|none|Description of the policy.| +|»» effect|string|false|none|Effect of the policy| +|»» id|string|false|none|ID of the policy.| +|»» resources|[string]|false|none|Resources impacted by the policy.| +|»» subjects|[string]|false|none|Subjects impacted by the policy.| + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +[ + { + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] + } +] +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /policies \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/policies", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/policies', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/policies"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/policies', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/policies', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### createPolicy + +``` +POST /policies HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Create an Access Control Policy + +#### Request body + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[policy](#schemapolicy)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|policy|[policy](#schemapolicy)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 201 response + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /policies \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/policies", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/policies', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/policies"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/policies', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/policies', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### getPolicy + +``` +GET /policies/{id} HTTP/1.1 +Accept: application/json + +``` + +Get an Access Control Policy + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the policy.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|policy|[policy](#schemapolicy)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /policies/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/policies/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/policies/{id}', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/policies/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/policies/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/policies/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### updatePolicy + +``` +PUT /policies/{id} HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Update an Access Control Policy + +#### Request body + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the policy.| +|body|body|[policy](#schemapolicy)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|policy|[policy](#schemapolicy)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /policies/{id} \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/policies/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/policies/{id}', { + method: 'PUT', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/policies/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.put( + '/policies/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.put '/policies/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### deletePolicy + +``` +DELETE /policies/{id} HTTP/1.1 +Accept: application/json + +``` + +Delete an Access Control Policy + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the policy.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /policies/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/policies/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/policies/{id}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/policies/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/policies/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/policies/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## role + + + +### List all roles + +``` +GET /roles HTTP/1.1 +Accept: application/json + +``` + +A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +This endpoint allows you to retrieve all roles that are stored in the system. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|member|query|string|false|The id of the member to look up.| +|limit|query|integer(int64)|false|The maximum amount of policies returned.| +|offset|query|integer(int64)|false|The offset from where to start looking.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A list of roles the member is belonging to|Inline| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[[role](#schemarole)]|false|none|[Role represents a group of users that share the same role. A role could be an administrator, a moderator, a regular user or some other sort of role.]| +|» id|string|false|none|ID is the role's unique id.| +|» members|[string]|false|none|Members is who belongs to the role.| + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +[ + { + "id": "string", + "members": [ + "string" + ] + } +] +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /roles \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/roles", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/roles', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/roles', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/roles', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Create a role + +``` +POST /roles HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +This endpoint allows you to create a new role. You may define members as well but you don't have to. + +#### Request body + +```json +{ + "id": "string", + "members": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[role](#schemarole)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|role|[role](#schemarole)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 201 response + +```json +{ + "id": "string", + "members": [ + "string" + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /roles \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/roles", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "id": "string", + "members": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/roles', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/roles', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/roles', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Get a role by its ID + +``` +DELETE /roles/{id} HTTP/1.1 +Accept: application/json + +``` + +A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +This endpoint allows you to delete an existing role. You have to know the role's ID. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the role to look up.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /roles/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/roles/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/roles/{id}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/roles/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/roles/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +``` +PUT /roles/{id} HTTP/1.1 +Accept: application/json + +``` + +This endpoint allows you to overwrite a role. You have to know the role's ID. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /roles/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/roles/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/roles/{id}', { + method: 'PUT', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.put( + '/roles/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.put '/roles/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Add members to a role + +``` +POST /roles/{id}/members HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +This endpoint allows you to add members (users, applications, ...) to a specific role. You have to know the role's ID. + +#### Request body + +```json +{ + "members": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the role to modify.| +|body|body|[roleMembers](#schemarolemembers)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /roles/{id}/members \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/roles/{id}/members", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "members": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/roles/{id}/members', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles/{id}/members"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/roles/{id}/members', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/roles/{id}/members', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Remove members from a role + +``` +DELETE /roles/{id}/members HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +A Role represents a group of users that share the same role and thus permissions. A role could be an administrator, a moderator, a regular +user or some other sort of role. + +This endpoint allows you to remove members (users, applications, ...) from a specific role. You have to know the role's ID. + +#### Request body + +```json +{ + "members": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|The id of the role to modify.| +|body|body|[roleMembers](#schemarolemembers)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /roles/{id}/members \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/roles/{id}/members", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "members": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/roles/{id}/members', { + method: 'DELETE', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/roles/{id}/members"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.delete( + '/roles/{id}/members', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.delete '/roles/{id}/members', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## version + + + +### Get the version of Keto + +``` +GET /version HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns the version as `{ "version": "VERSION" }`. The version is only correct with the prebuilt binary and not custom builds. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|version|[version](#schemaversion)| + +##### Examples + +###### 200 response + +```json +{ + "version": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /version \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/version", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/version', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/version"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/version', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/version', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## warden + + + +### Check if an OAuth 2.0 access token is authorized to access a resource + +``` +POST /warden/oauth2/access-tokens/authorize HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Checks if a token is valid and if the token subject is allowed to perform an action on a resource. +This endpoint requires a token, a scope, a resource name, an action name and a context. + +If a token is expired/invalid, has not been granted the requested scope or the subject is not allowed to +perform the action on the resource, this endpoint returns a 200 response with `{ "allowed": false }`. + +This endpoint passes all data from the upstream OAuth 2.0 token introspection endpoint. If you use ORY Hydra as an +upstream OAuth 2.0 provider, data set through the `accessTokenExtra` field in the consent flow will be included in this +response as well. + +#### Request body + +```json +{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ], + "token": "string" +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[wardenOAuth2AccessTokenAuthorizationRequest](#schemawardenoauth2accesstokenauthorizationrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|wardenOAuth2AccessTokenAuthorizationResponse|[wardenOAuth2AccessTokenAuthorizationResponse](#schemawardenoauth2accesstokenauthorizationresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "allowed": true, + "aud": [ + "string" + ], + "client_id": "string", + "exp": "2018-11-15T08:23:48Z", + "iat": "2018-11-15T08:23:48Z", + "iss": "string", + "nbf": "2018-11-15T08:23:48Z", + "scope": "string", + "session": { + "property1": {}, + "property2": {} + }, + "sub": "string", + "username": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /warden/oauth2/access-tokens/authorize \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/warden/oauth2/access-tokens/authorize", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ], + "token": "string" +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/warden/oauth2/access-tokens/authorize', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/warden/oauth2/access-tokens/authorize"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/warden/oauth2/access-tokens/authorize', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/warden/oauth2/access-tokens/authorize', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Check if an OAuth 2.0 Client is authorized to access a resource + +``` +POST /warden/oauth2/clients/authorize HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Checks if an OAuth 2.0 Client provided the correct access credentials and and if the client is allowed to perform +an action on a resource. This endpoint requires a client id, a client secret, a scope, a resource name, an action name and a context. + +#### Request body + +```json +{ + "action": "string", + "client_id": "string", + "client_secret": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ] +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[wardenOAuth2ClientAuthorizationRequest](#schemawardenoauth2clientauthorizationrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|wardenOAuth2ClientAuthorizationResponse|[wardenOAuth2ClientAuthorizationResponse](#schemawardenoauth2clientauthorizationresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "allowed": true, + "sub": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /warden/oauth2/clients/authorize \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/warden/oauth2/clients/authorize", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "action": "string", + "client_id": "string", + "client_secret": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ] +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/warden/oauth2/clients/authorize', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/warden/oauth2/clients/authorize"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/warden/oauth2/clients/authorize', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/warden/oauth2/clients/authorize', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Check if a subject is authorized to access a resource + +``` +POST /warden/subjects/authorize HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Checks if a subject (e.g. user ID, API key, ...) is allowed to perform a certain action on a resource. + +#### Request body + +```json +{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "subject": "string" +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[WardenSubjectAuthorizationRequest](#schemawardensubjectauthorizationrequest)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|wardenSubjectAuthorizationResponse|[wardenSubjectAuthorizationResponse](#schemawardensubjectauthorizationresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "allowed": true, + "sub": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /warden/subjects/authorize \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/warden/subjects/authorize", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "subject": "string" +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/warden/subjects/authorize', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/warden/subjects/authorize"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/warden/subjects/authorize', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/warden/subjects/authorize', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ +## Schemas + +AuthenticationOAuth2ClientCredentialsRequest +#### AuthenticationOAuth2ClientCredentialsRequest + + + +```json +{ + "client_id": "string", + "client_secret": "string", + "scope": [ + "string" + ] +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|client_id|string|false|none|Token is the token to introspect.| +|client_secret|string|false|none|none| +|scope|[string]|false|none|Scope is an array of scopes that are required.| + +AuthenticationOAuth2IntrospectionRequest +#### AuthenticationOAuth2IntrospectionRequest + + + +```json +{ + "scope": [ + "string" + ], + "token": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|scope|[string]|false|none|Scope is an array of scopes that are required.| +|token|string|false|none|Token is the token to introspect.| + +Authenticator +#### Authenticator + + + +```json +{} + +``` + +#### Properties + +*None* + +Firewall +#### Firewall + + + +```json +{} + +``` + +*Firewall offers various validation strategies for access tokens.* + +#### Properties + +*None* + +Handler +#### Handler + + + +```json +{ + "H": {}, + "Manager": {} +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|H|[Writer](#schemawriter)|false|none|Writer is a helper to write arbitrary data to a ResponseWriter| +|Manager|[Manager](#schemamanager)|false|none|none| + +IntrospectionResponse +#### IntrospectionResponse + + + +```json +{ + "active": true, + "aud": [ + "string" + ], + "client_id": "string", + "exp": 0, + "ext": { + "property1": {}, + "property2": {} + }, + "iat": 0, + "iss": "string", + "nbf": 0, + "scope": "string", + "sub": "string", + "token_type": "string", + "username": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|active|boolean|false|none|none| +|aud|[string]|false|none|none| +|client_id|string|false|none|none| +|exp|integer(int64)|false|none|none| +|ext|object|false|none|Session represents arbitrary session data.| +|» **additionalProperties**|object|false|none|none| +|iat|integer(int64)|false|none|none| +|iss|string|false|none|none| +|nbf|integer(int64)|false|none|none| +|scope|string|false|none|none| +|sub|string|false|none|Here, it's sub| +|token_type|string|false|none|none| +|username|string|false|none|none| + +Manager +#### Manager + + + +```json +{} + +``` + +#### Properties + +*None* + +OAuth2ClientCredentialsAuthentication +#### OAuth2ClientCredentialsAuthentication + + + +```json +{} + +``` + +#### Properties + +*None* + +OAuth2IntrospectionAuthentication +#### OAuth2IntrospectionAuthentication + + + +```json +{} + +``` + +#### Properties + +*None* + +Session +#### Session + + + +```json +{ + "GetSubject": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|GetSubject|string|false|none|none| + +WardenSubjectAuthorizationRequest +#### WardenSubjectAuthorizationRequest + + + +```json +{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "subject": "string" +} + +``` + +*AccessRequest is the warden's request object.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|action|string|false|none|Action is the action that is requested on the resource.| +|context|object|false|none|Context is the request's environmental context.| +|» **additionalProperties**|object|false|none|none| +|resource|string|false|none|Resource is the resource that access is requested to.| +|subject|string|false|none|Subejct is the subject that is requesting access.| + +Writer +#### Writer + + + +```json +{} + +``` + +*Writer is a helper to write arbitrary data to a ResponseWriter* + +#### Properties + +*None* + +authenticationDefaultSession +#### authenticationDefaultSession + + + +```json +{ + "allowed": true, + "sub": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| + +authenticationOAuth2ClientCredentialsSession +#### authenticationOAuth2ClientCredentialsSession + + + +```json +{ + "allowed": true, + "sub": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| + +authenticationOAuth2Session +#### authenticationOAuth2Session + + + +```json +{ + "allowed": true, + "aud": [ + "string" + ], + "client_id": "string", + "exp": "2018-11-15T08:23:48Z", + "iat": "2018-11-15T08:23:48Z", + "iss": "string", + "nbf": "2018-11-15T08:23:48Z", + "scope": "string", + "session": { + "property1": {}, + "property2": {} + }, + "sub": "string", + "username": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|aud|[string]|false|none|none| +|client_id|string|false|none|ClientID is the id of the OAuth2 client that requested the token.| +|exp|string(date-time)|false|none|ExpiresAt is the expiry timestamp.| +|iat|string(date-time)|false|none|IssuedAt is the token creation time stamp.| +|iss|string|false|none|Issuer is the id of the issuer, typically an hydra instance.| +|nbf|string(date-time)|false|none|none| +|scope|string|false|none|GrantedScopes is a list of scopes that the subject authorized when asked for consent.| +|session|object|false|none|Session represents arbitrary session data.| +|» **additionalProperties**|object|false|none|none| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| +|username|string|false|none|none| + +healthNotReadyStatus +#### healthNotReadyStatus + + + +```json +{ + "errors": { + "property1": "string", + "property2": "string" + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|errors|object|false|none|Errors contains a list of errors that caused the not ready status.| +|» **additionalProperties**|string|false|none|none| + +healthStatus +#### healthStatus + + + +```json +{ + "status": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|Status always contains "ok".| + +policy +#### policy + + + +```json +{ + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|actions|[string]|false|none|Actions impacted by the policy.| +|conditions|object|false|none|Conditions under which the policy is active.| +|» **additionalProperties**|object|false|none|none| +|»» options|object|false|none|none| +|»»» **additionalProperties**|object|false|none|none| +|»» type|string|false|none|none| +|» description|string|false|none|Description of the policy.| +|» effect|string|false|none|Effect of the policy| +|» id|string|false|none|ID of the policy.| +|» resources|[string]|false|none|Resources impacted by the policy.| +|» subjects|[string]|false|none|Subjects impacted by the policy.| + +role +#### role + + + +```json +{ + "id": "string", + "members": [ + "string" + ] +} + +``` + +*Role represents a group of users that share the same role. A role could be an administrator, a moderator, a regular +user or some other sort of role.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|ID is the role's unique id.| +|members|[string]|false|none|Members is who belongs to the role.| + +roleMembers +#### roleMembers + + + +```json +{ + "members": [ + "string" + ] +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|members|[string]|false|none|none| + +swaggerCreatePolicyParameters +#### swaggerCreatePolicyParameters + + + +```json +{ + "Body": { + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[policy](#schemapolicy)|false|none|none| + +swaggerDoesWardenAllowAccessRequestParameters +#### swaggerDoesWardenAllowAccessRequestParameters + + + +```json +{ + "Body": { + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "subject": "string" + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[WardenSubjectAuthorizationRequest](#schemawardensubjectauthorizationrequest)|false|none|none| + +swaggerDoesWardenAllowClientRequestParameters +#### swaggerDoesWardenAllowClientRequestParameters + + + +```json +{ + "Body": { + "action": "string", + "client_id": "string", + "client_secret": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ] + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[wardenOAuth2ClientAuthorizationRequest](#schemawardenoauth2clientauthorizationrequest)|false|none|none| + +swaggerDoesWardenAllowTokenAccessRequestParameters +#### swaggerDoesWardenAllowTokenAccessRequestParameters + + + +```json +{ + "Body": { + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ], + "token": "string" + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[wardenOAuth2AccessTokenAuthorizationRequest](#schemawardenoauth2accesstokenauthorizationrequest)|false|none|none| + +swaggerGetPolicyParameters +#### swaggerGetPolicyParameters + + + +```json +{ + "id": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|The id of the policy. in: path| + +swaggerListPolicyParameters +#### swaggerListPolicyParameters + + + +```json +{ + "limit": 0, + "offset": 0 +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|limit|integer(int64)|false|none|The maximum amount of policies returned. in: query| +|offset|integer(int64)|false|none|The offset from where to start looking. in: query| + +swaggerListPolicyResponse +#### swaggerListPolicyResponse + + + +```json +{ + "Body": [ + { + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] + } + ] +} + +``` + +*A policy* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[[policy](#schemapolicy)]|false|none|in: body type: array| + +swaggerUpdatePolicyParameters +#### swaggerUpdatePolicyParameters + + + +```json +{ + "Body": { + "actions": [ + "string" + ], + "conditions": { + "property1": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + }, + "property2": { + "options": { + "property1": {}, + "property2": {} + }, + "type": "string" + } + }, + "description": "string", + "effect": "string", + "id": "string", + "resources": [ + "string" + ], + "subjects": [ + "string" + ] + }, + "id": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[policy](#schemapolicy)|false|none|none| +|id|string|false|none|The id of the policy. in: path| + +swaggerWardenBaseRequest +#### swaggerWardenBaseRequest + + + +```json +{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string" +} + +``` + +*swager:model authorizedBaseRequest* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|action|string|false|none|Action is the action that is requested on the resource.| +|context|object|false|none|Context is the request's environmental context.| +|» **additionalProperties**|object|false|none|none| +|resource|string|false|none|Resource is the resource that access is requested to.| + +version +#### version + + + +```json +{ + "version": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|version|string|false|none|none| + +wardenOAuth2AccessTokenAuthorizationRequest +#### wardenOAuth2AccessTokenAuthorizationRequest + + + +```json +{ + "action": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ], + "token": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|action|string|false|none|Action is the action that is requested on the resource.| +|context|object|false|none|Context is the request's environmental context.| +|» **additionalProperties**|object|false|none|none| +|resource|string|false|none|Resource is the resource that access is requested to.| +|scope|[string]|false|none|Scope is an array of scopes that are required.| +|token|string|false|none|Token is the token to introspect.| + +wardenOAuth2AccessTokenAuthorizationResponse +#### wardenOAuth2AccessTokenAuthorizationResponse + + + +```json +{ + "allowed": true, + "aud": [ + "string" + ], + "client_id": "string", + "exp": "2018-11-15T08:23:48Z", + "iat": "2018-11-15T08:23:48Z", + "iss": "string", + "nbf": "2018-11-15T08:23:48Z", + "scope": "string", + "session": { + "property1": {}, + "property2": {} + }, + "sub": "string", + "username": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|aud|[string]|false|none|none| +|client_id|string|false|none|ClientID is the id of the OAuth2 client that requested the token.| +|exp|string(date-time)|false|none|ExpiresAt is the expiry timestamp.| +|iat|string(date-time)|false|none|IssuedAt is the token creation time stamp.| +|iss|string|false|none|Issuer is the id of the issuer, typically an hydra instance.| +|nbf|string(date-time)|false|none|none| +|scope|string|false|none|GrantedScopes is a list of scopes that the subject authorized when asked for consent.| +|session|object|false|none|Session represents arbitrary session data.| +|» **additionalProperties**|object|false|none|none| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| +|username|string|false|none|none| + +wardenOAuth2ClientAuthorizationRequest +#### wardenOAuth2ClientAuthorizationRequest + + + +```json +{ + "action": "string", + "client_id": "string", + "client_secret": "string", + "context": { + "property1": {}, + "property2": {} + }, + "resource": "string", + "scope": [ + "string" + ] +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|action|string|false|none|Action is the action that is requested on the resource.| +|client_id|string|false|none|Token is the token to introspect.| +|client_secret|string|false|none|none| +|context|object|false|none|Context is the request's environmental context.| +|» **additionalProperties**|object|false|none|none| +|resource|string|false|none|Resource is the resource that access is requested to.| +|scope|[string]|false|none|Scope is an array of scopes that are required.| + +wardenOAuth2ClientAuthorizationResponse +#### wardenOAuth2ClientAuthorizationResponse + + + +```json +{ + "allowed": true, + "sub": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| + +wardenSubjectAuthorizationResponse +#### wardenSubjectAuthorizationResponse + + + +```json +{ + "allowed": true, + "sub": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|allowed|boolean|false|none|Allowed is true if the request is allowed and false otherwise.| +|sub|string|false|none|Subject is the identity that authorized issuing the token, for example a user or an OAuth2 app. This is usually a uuid but you can choose a urn or some other id too.| + diff --git a/docs/oathkeeper/4-deployment.md.bak b/docs/oathkeeper/4-deployment.md.bak new file mode 100644 index 000000000..ee9f6bdeb --- /dev/null +++ b/docs/oathkeeper/4-deployment.md.bak @@ -0,0 +1,40 @@ +# Deployment + +**THIS SECTION IS OUTDATED AND TBD** + + + +ORY Oathkeeper is developed as a native cloud application. As such, it is completely stateless, does not require +configuration files and does not persist anything to disk. It is possible to restart ORY Oathkeeper without any side-effects +as ORY Oathkeeper supports graceful HTTP shutdown. + +ORY Oathkeeper works natively with Load Balancers and auto-scaling groups. It is configured using environment variables +and relies on a PostgreSQL / MySQL database instance. To answer access requests quickly, rules are cached in-memory +and refreshed within a given interval (e.g. 30 seconds). + +ORY Oathkeeper has a tiny footprint as it is compiled to native bytecode. The docker image is a mere 5 MB, the memory +footprint similarly low and the CPU usage is, on average, close to zero. + +Thus, two possible deployment layouts are possible for ORY Oathkeeper, which are covered in the next two sections. + +## Gateway + +In the gateway deployment scenario, ORY Oathkeeper is the single point of entry for all API endpoints. Oathkeeper might +sit behind a load balancer, but is generally deployed in front of the API router. Any incoming requests first +pass ORY Oathkeeper, then the API router, and finally reach the API endpoint service. + +![Gateway Oathkeeper Deployment Layout](../images/gateway_deployment.svg) + +The advantage of this deployment layout is that there is only one deployment of ORY Oathkeeper that needs to be maintained. +The disadvantage is that it is not possible to talk to API endpoints directly without going through the full +reverse proxy chain. + +## Sidecar + +In the sidecar deployment scenario, ORY Oathkeeper is deployed alongside each API endpoint service, probably even +within the same logical unit (e.g. Docker Container or VM) as the service itself. + +![Gateway Oathkeeper Deployment Layout](../images/sidecar_deployment.svg) + +The advantage of this deployment layout is that requests to the API endpoints are possible without passing through +the API router. The disadvantage is that multiple instances of ORY Oathkeeper have to be maintained. diff --git a/docs/oathkeeper/api-access-rules.md b/docs/oathkeeper/api-access-rules.md new file mode 100644 index 000000000..ddefeb8f2 --- /dev/null +++ b/docs/oathkeeper/api-access-rules.md @@ -0,0 +1,798 @@ +--- +id: oathkeeper-api-access-rules +title: API Access Rules +--- + +ORY Oathkeeper decides whether or not access should be allowed with Access Rules. Access Rules can be managed using the +ORY Oathkeeper API. + +## Overview + +An ORY Oathkeeper Access Rule has the following layout: + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": {/* ... */}, + "credentials_issuer": { /* ... */ } +} +``` + +In this case, if a request to `http://my-app/some-route` is made (this is where ORY Oathkeeper will listen to), then +the rule with ID `some-id` will be executed. Then: + +* If the request hits the ORY Oathkeeper proxy (`oathkeeper serve proxy`): The request will be forwarded to the upstream URL. +* If the request hits the ORY Oathkeeper judge (`oathkeeper serve api` -> `/judge/some-route`): The server will respond with +status code 200 if the request is valid and any other status code if not. + +The `match.url` value is capable of parsing regular expressions. Value `http://my-app/some-route` will only +match this exact URL, not `http://my-app/some-route/foo`, `http://my-app/some-ROUTE`, nor `https://my-app/some-route`. + +Regular expressions are delimited with `<` and `>`. A `match.url` value of `http://my-app/some-route<.*>` will match +`http://my-app/some-route/foo`, `http://my-app/some-route`, `http://my-app/some-routeABCDEF`, and so on. You can +use multiple regular expressions: `://my-app/some-route<.*>`. + +The `match.methods` value defines which HTTP methods this access rule will match. The rule `some-id` will, for example, +not match POST requests. + +ORY Oathkeeper will throw an error if more than one access rule is found for a given HTTP request. You have to make sure +that your access rules don't overlap. + +Once an access rule is matched, ORY Oathkeeper will authenticate the credentials, authorize the request subject (e.g. the user), +and transform the credentials. More on that in the next sections. + +### Authenticators + +An authenticator is responsible for authenticating request credentials. ORY Oathkeeper supports different authenticators +and we will add more as the project progresses. + +An authenticator inspects the HTTP request (e.g. the HTTP Authorization Header) and executes some business logic that +returns true (for authentication ok) or false (for authentication invalid) as well as a subject ID. The subject ID is +typically the "user" that made the request, but it could also be a machine (if you have machine-2-machine interaction) or +something different. + +Each authenticator has the same configuration layout + +``` +"authenticators": [ + { + "handler": "a", + "config": {/* depends on the authenticator */} + } +] +``` + +where `handler` is the name of the authenticator and `config` is an optional configuration for that specific +authenticator. + +You can define more than one authenticator in the access rule. The first authenticator that is able to handle the +credentials will be consulted and other authenticators will be ignored: + +``` +"authenticators": [ + { + "handler": "a" + }, + { + "handler": "b" + }, + { + "handler": "c" + } +] +``` + +If handler `a` is able to handle the provided credentials, then handler `b` and `c` will be ignored. If handler `a` +can not handle the provided credentials but handler `b` can, then handler `a` and `c` will be ignored. Handling +the provided credentials means that the authenticator knows how to handle, for example, the `Authorization: basic` header. +It does not mean that the credentials are valid! If a handler encounters invalid credentials, then other handlers will be ignored too. + + +Let's take a look at the different supported authenticators! + +#### `noop` + +This is a special authenticator. It tells ORY Oathkeeper to bypass the authentication process. This also implies +that no authorization will be executed and no credentials will be issued. It's basically a pass-all authenticator +that allows any request to be forwarded to the upstream URL. + +##### Example + +The following rule allows all requests to `GET http://my-app/some-route`: + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [{ + "handler": "noop" + }] +} +``` + +#### `anonymous` + +The `anonymous` authenticator checks whether or not an `Authorization` header is set. If not, it will use the value +of the environment variable `AUTHENTICATOR_ANONYMOUS_USERNAME` as the subject name. + +##### Example + +The following rule allows all requests to `GET http://my-app/some-route` and sets the subject name to the value +of the environment variable `AUTHENTICATOR_ANONYMOUS_USERNAME`, as long as no `Authorization` header is set in the +HTTP request: + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [{ + "handler": "anonymous" + }], + /* ... */ +} +``` + +#### `oauth2_client_credentials` + +This `oauth2_client_credentials` uses the username and password from HTTP Basic Authorization (`Authorization: basic base64()` +to perform the OAuth 2.0 Client Credentials grant in order to detect if the provided credentials are valid. + +To use this authenticator, you must provide the environment variable `AUTHENTICATOR_OAUTH2_CLIENT_CREDENTIALS_TOKEN_URL` +which sets the OAuth 2.0 Token URL that should be used to check if the provided credentials are valid or not. For +example, you could set it as follows: + +``` +$ export AUTHENTICATOR_OAUTH2_CLIENT_CREDENTIALS_TOKEN_URL=https://my-oauth2-server/oauth2/token +``` + +This authenticator will use the username from the HTTP Basic Authorization header as the subject for this request. + +This authenticator has one configuration option which is `required_scope`. This option sets what scope is +required by the URL and when making performing OAuth 2.0 Client Credentials request, the scope will be included +in the request. + +##### Example + +The following rule allows requests to `GET http://my-app/some-route` if valid a OAuth 2.0 Client ID and Client Secret +has been provided and if that client is allowed to request scope `scope-a` and `scope-b`: + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [{ + "handler": "oauth2_client_credentials", + "config": { + "required_scope": ["scope-a", "scope-b"] + } + }], + /* ... */ +} +``` + +#### `oauth2_introspection` + +The `oauth2_introspection` authenticator handles requests that have an Bearer Token in the Authorization Header (`Authorization: bearer `). +It then uses OAuth 2.0 Token Introspection to check if the token is valid and if the token was granted the requested scope. + +This authenticator is a bit more complex to set up. You have to define the following environment variables: + +* Required + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_URL`: The OAuth 2.0 Token Introspection URL. +* In cases where the OAuth 2.0 Introspection Endpoint is protected and requires an OAuth 2.0 Access Token, you can configure +ORY Oathkeeper to authorize before making requests to that endpoint with the following, optional, settings: + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_CLIENT_ID`: The OAuth 2.0 Client ID the client that performs the OAuth 2.0 Token Introspection. The OAuth 2.0 Token Introspection + endpoint is typically protected and requires a valid OAuth 2.0 Client in order to check if a token is valid or not. + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_CLIENT_SECRET`: The OAuth 2.0 Client Secret of the client that performs the OAuth 2.0 Token Introspection. + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_TOKEN_URL`: The OAuth 2.0 Token URL. + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE`: If the OAuth 2.0 Token Introspection endpoint requires a certain OAuth 2.0 Scope + in order to be accessed, you can set it using this environment variable. Use commas to define more than one OAuth 2.0 Scope. + Example: `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE=scope-a,scope-b` + * `AUTHENTICATOR_OAUTH2_INTROSPECTION_SCOPE_STRATEGY`: The strategy to be used to validate the scope claim. Strategies `HIERARCHIC`, `EXACT`, + `WILDCARD`, `NONE` are supported. Defaults to `EXACT`. For more information on scope strategies, click [here](#scope-strategies)- + + +##### Example + +The following rule allows requests to `GET http://my-app/some-route` if valid a OAuth 2.0 Access Token was provided +and if that token was granted OAuth 2.0 Scope `scope-a` and `scope-b`: + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [{ + "handler": "oauth2_introspection", + "config": { + "required_scope": ["scope-a", "scope-b"] + } + }], + /* ... */ +} +``` + +#### `jwt` + +The `jwt` authenticator handles requests that have an Bearer Token in the Authorization Header (`Authorization: bearer `). +It assumes that the token is a JSON Web Token and tries to verify the signature of it. + +* Required + * `AUTHENTICATOR_JWT_JWKS_URL`: The URL where ORY Oathkeeper can retrieve JSON Web Keys from for validating + the JSON Web Token. Usually something like `https://my-keys.com/.well-known/jwks.json`. The response + of that endpoint must return a [JSON Web Key Set (JWKS)](https://auth0.com/docs/jwks). +* Optional + * `AUTHENTICATOR_JWT_SCOPE_STRATEGY`: The strategy to be used to validate the scope claim. Strategies `HIERARCHIC`, `EXACT`, + `WILDCARD`, `NONE` are supported. Defaults to `EXACT`. For more information on scope strategies, click [here](#scope-strategies)- + +##### Example + +The following rule allows requests to `GET http://my-app/some-route` if valid a JSON Web Token was provided +and if that token has scope `scope-a` and `scope-b`, audience `aud-1` and was issued by `iss-1`. All configuration +items are optional and ignored if left out. + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [{ + "handler": "jwt", + "config": { + "required_scope": ["scope-a", "scope-b"], + "require_audience": ["scope-a", "scope-b"], + "trusted_issuers": ["scope-a", "scope-b"], + } + }], + /* ... */ +} +``` + +Per default, this authenticator accepts JSON Web Tokens signed with the `RS256` algorithm only. You can change that +by whitelisting the algorithms to be allowed. All common JWT singing algorihtms (except `none`) are supported: + +``` +{ + /* ... */ + "authenticators": [{ + "handler": "jwt", + "config": { + "allowed_algorithms": ["RS256", "HS256", "RS512", "ES256"] + } + }], + /* ... */ +} +``` + +### Authorizers + +The authorizer takes the subject returned from the authenticator and checks if that subject is allowed to perform +the requested action. + +Each authorizer has the same configuration layout + +``` +"authorizer": { + "handler": "a", + "config": {/* depends on the authorizer */} +} +``` + +#### `allow` + +This authorizer allows every request to pass. + +#### Example + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": { + "handler": "allow" + } + /* ... */ +} +``` + +#### `deny` + +This authorizer denies every request. + +#### Example + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/some-route", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": { + "handler": "deny" + } + /* ... */ +} +``` + +#### `keto_warden` + +This authorizer uses the ORY Keto Warden API to perform sophisticated access control with access control policies. +Please familiarize yourself with the ORY Keto project before you set up this authorizer. + +To configure this authorizer, you must set the environment variable `AUTHORIZER_KETO_WARDEN_KETO_URL` to ORY Keto's URL, +for example `AUTHORIZER_KETO_WARDEN_KETO_URL=http://keto/`. **If this environment variable is not set, then this authorizer +will be disabled.** + +This authorizer has three configuration options, `required_action`, `required_resource` and `subject`: + +``` +"authorizer": { + "handler": "keto_warden", + "config": { + "required_action": "...", + "required_resource": "...", + "subject": "..." + } +} +``` + +These configuration options support variable expansion. Let's say you have the following match configuration: + +``` +"match": { + "url": "http://my-app/api/users/<[0-9]+>/<[a-zA-Z]+>", + "methods": ["GET"] +}, +``` + +Here, you have two regular expressions, `<[0-9]+>` and `<[a-zA-Z]+>`. You can reference the values matched by the +regular expression with `$1` and `$2` (and more generally `$n`) and they will be substituted before making the request +to ORY Keto: + +``` +"config": { + "required_action": "my:action:$1", + "required_resource": "my:resource:$2:foo:$1" +} +``` + +Assuming a request to `http://my-api/api/users/1234/foobar` was made, the config from above would expand to: + +``` +"config": { + "required_action": "my:action:1234", + "required_resource": "my:resource:foobar:foo:1234" +} +``` + +The `subject` field configures what subject is passed on to keto warden. +The `subject` value is a string which will be parsed by the Go [`text/template`](https://golang.org/pkg/text/template/) +package for value substitution, receiving the [`AuthenticationSession`](https://github.com/ory/oathkeeper/blob/92c09fb28552949cd034ed5555c87dfda91407a3/proxy/authenticator.go#L19) +struct: + +```go +type AuthenticationSession struct { + Subject string + Extra map[string]interface{} +} +``` + +If `subject` is not specified it will default to `AuthenticationSession.Subject`. + +Note that the `AuthenticationSession` struct has a field named `Extra` which is a `map[string]interface{}`, which receives +varying introspection data from the authentication process. Because the contents of `Extra` are so variable, nested and +potentially non-existent values need special handling by the `text/template` parser, and a `print` FuncMap function has +been provided to ensure that non-existent map values will simply return an empty string, rather than ``. + +If you find that your headers contain the string `` then you have most likely omitted the `print` function, and +it is recommended you use it for all values out of an abundance of caution and for consistency. + +#### Example + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/api/users/<[0-9]+>/<[a-zA-Z]+>", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": { + "handler": "keto_warden", + "config": { + "required_action": "my:action:$1", + "required_resource": "my:resource:$2:foo:$1" + "subject": "{{ .Extra.email }}" + } + } + /* ... */ +} +``` + + +### Credentials Issuers + +A credentials issuer translates the credentials from incoming requests to credentials that you backend understands. +For example, the `Authorization: basic` header might be transformed to `X-User: `. This allows you to +write backends that do not care if the original request was an anonymous one, an OAuth 2.0 Access Token, or some other +credential type. All your backend has to do is understand, for example, the `X-User:`. + +If you access ORY Oathkeeper using the judge endpoint, the header will be included in the HTTP response. + +#### `noop` + +This credentials issuer does not transform the HTTP request and simply forwards the headers as-is. This is useful +if you don't want to replace, for example, `Authorization: basic` with `X-User: `. + +##### Example + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/api/users/<[0-9]+>/<[a-zA-Z]+>", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": {/* ... */ } + "credentials_issuer" { + "handler": "noop" + } +} +``` + +#### `id_token` + +This credentials issuer takes the authentication information (e.g. subject ID) and transforms it to a signed JSON Web Token, +and more specifically to an OpenID Connect ID Token. You backend can verify the token by fetching the (public) key +from the `/.well-known/jwks.json` endpoint. + +Let's say a request is made to a resource protected by ORY Oathkeeper: + +``` +GET /api/resource HTTP/1.1 +Host: www.example.com +Authorization: bearer +``` + +Assuming that ORY Oathkeeper is granting the access request, the `` will be replaced with a +JSON Web Token that is signed using the asymmetric RS256 key: + +``` +GET /api/resource HTTP/1.1 +Host: internal-api-endpoint-dns +Authorization: bearer +``` + +Now, the protected resource is capable of decoding and validating the JSON Web Token using the public key supplied +by ORY Oathkeeper's API. The public key for decoding the ID token is available at Oathkeeper's `/.well-known/jwks.json` endpoint: + +``` +http://oathkeeper:4456/.well-known/jwks.json +``` + +That `.well-known/jwks.json` endpoint is not served by the ORY Oathkeeper proxy process, but by +the ORY Oathkeeper API process. You will learn more about the difference between the two in the next chapters. + +The related flow diagram looks like this: + +![ID Token Transformation](../../images/docs/oathkeeper/id_token.svg) + +Let's say the `oauth2_client_credentials` authenticator successfully authenticated the credentials `client-id:client-secret`. +This credentials issuer will craft an ID Token (JWT) with the following exemplary claims: + +``` +{ + "iss": "https://server.example.com", + "sub": "client-id", + "aud": "s6BhdRkqt3", + "jti": "n-0S6_WzA2Mj", + "exp": 1311281970, + "iat": 1311280970, +} +``` + +The ID Token Claims are as follows: + +* `iss`: Issuer Identifier for the Issuer of the response. The iss value is a case sensitive URL using the https scheme + that contains scheme, host, and optionally, port number and path components and no query or fragment components. + Typically, this is the URL of ORY Oathkeeper, for example: `https://oathkeeper.myapi.com`. +* `sub`: Subject Identifier. A locally unique and never reassigned identifier within the Issuer for the End-User, which + is intended to be consumed by the Client, e.g., 24400320 or AItOawmwtWwcT0k51BayewNvutrJUqsvl6qs7A4. It must not + exceed 255 ASCII characters in length. The sub value is a case sensitive string. The End-User might also + be an OAuth 2.0 Client, given that the access token was granted using the OAuth 2.0 Client Credentials flow. +* `aud`: Audience(s) that this ID Token is intended for. It MUST contain the OAuth 2.0 client_id of the Relying Party + as an audience value. It MAY also contain identifiers for other audiences. In the general case, the aud value is an + array of case sensitive strings. +* `exp`: Expiration time on or after which the ID Token MUST NOT be accepted for processing. The processing of this + parameter requires that the current date/time MUST be before the expiration date/time listed in the value. + Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z as measured in UTC until the + date/time. See RFC 3339 [RFC3339] for details regarding date/times in general and UTC in particular. +* `iat`: Time at which the JWT was issued. Its value is a JSON number representing the number of seconds + from 1970-01-01T0:0:0Z as measured in UTC until the date/time. +* `jti`: A cryptographically strong random identifier to ensure the ID Token's uniqueness. + +This credentials issuer implements several token signing algorithms, specifically: + +- `HS256`: This algorithm uses a HMAC-SHA256 with a shared secret as opposed to private/public keys. This strategy +is not encouraged for production. +- `ORY-HYDRA`: This algorithm uses ORY Hydra's JWK management API to generate private/public RSA keypair. This strategy +is encouraged for use in production. + +You can set the strategy using the `CREDENTIALS_ISSUER_ID_TOKEN_ALGORITHM` environment variable. There also two more environment variables which modify +the behaviour of this strategy: + +* `CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN`: The lifespan of the ID Token which defaults to 10 minutes. Example: + `CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN=1s` (1 second), `CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN=1m` (1 minute), + `CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN=1h` (1 hour), `CREDENTIALS_ISSUER_ID_TOKEN_LIFESPAN=1d` (1 day) +* `CREDENTIALS_ISSUER_ID_TOKEN_ISSUER`: Who issued the token - this will be the value of the `iss` claim in the + ID Token. + +##### Token Signing Algorithms + +###### `HS256` + +The HS256 algorithm is the default one. This algorithm requires you to set the secret to be used for signing the ID Token. +Be aware that anyone in possession of this secret - also anyone having access to the `./well-known/jwks.json` URL +will be able to forge ID Tokens that will be accepted by your backends. We recommend using this strategy primarily +for development purposes. + +**Do not use this strategy in production unless you know what you are doing.** If you do use this in production, make sure +that **noone** (except the services validating the tokens) have access to the `./well-known/jwks.json` URL. + +Use must use the `CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET` environment variable to set the secret. + +###### `ORY-HYDRA` + +This ID Token signing algorithm uses ORY Hydra's JSON Web Key API to generate, store, and fetch a RSA public/private keypair. +When using this algorithm, you have the following environment variables available: + +* Required + * `CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_ADMIN_URL`: The URL where the ORY Hydra Admin API is located. +* Optional + * `CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_JWK_SET_ID`: The ID to be used to create & fetch the JSON Web Key from ORY Hydra. + Defaults to `oathkeeper:id-token`. + * `CREDENTIALS_ISSUER_ID_TOKEN_JWK_REFRESH_INTERVAL`: ORY Oathkeeper stores JSON Web Keys for ID Token signing in memory. + This value sets the refresh interval. Default is 5 minutes. +* If this endpoint is protected using OAuth 2.0 Access Tokens, you can configure ORY Oathkeeper to authorize before +calling this endpoint. To do so, configure the following, optional settings: + * CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_CLIENT_id: oathkeeper- The ID of the OAuth 2.0 Client. + * CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_CLIENT_SECRET: The secret of the OAuth 2.0 Client. + * CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_CLIENT_SCOPES: The OAuth 2.0 Scope the client should request. + * CREDENTIALS_ISSUER_ID_TOKEN_HYDRA_PUBLIC_URL: The public URL where endpoint /oauth2/token is located. + +##### Access Rule Configuration + +Additionally, this credentials issuer allows you to specify the audience of the ID token per access rule. Setting +the audience is optional: + +``` +"credentials_issuer": { + "handler": "id_token", + "config": { + "aud": ["audience-1", "audience-2"] + } +} +``` + +##### Example + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/api/users/<[0-9]+>/<[a-zA-Z]+>", + "methods": [ + "GET" + ] + }, + "authenticators": [/* ... */], + "authorizer": {/* ... */ } + "credentials_issuer": { + "handler": "id_token", + "config": { + "aud": ["audience-1", "audience-2"] + } + } +} +``` + +#### `headers` + +This credentials issuer will transform the request, allowing you to pass the credentials to the upstream application via +the headers. This will augment, for example, `Authorization: basic` with `X-User: `. + +The headers are specified via the `headers` field of the credentials issuer's `config` field. The keys are the header +name and the values are a string which will be parsed by the Go [`text/template`](https://golang.org/pkg/text/template/) +package for value substitution, receiving the [`AuthenticationSession`](https://github.com/ory/oathkeeper/blob/92c09fb28552949cd034ed5555c87dfda91407a3/proxy/authenticator.go#L19) +struct: + +```go +type AuthenticationSession struct { + Subject string + Extra map[string]interface{} +} +``` + +Note that the `AuthenticationSession` struct has a field named `Extra` which is a `map[string]interface{}`, which receives +varying introspection data from the authentication process. Because the contents of `Extra` are so variable, nested and +potentially non-existent values need special handling by the `text/template` parser, and a `print` FuncMap function has +been provided to ensure that non-existent map values will simply return an empty string, rather than ``. + +If you find that your headers contain the string `` then you have most likely omitted the `print` function, and +it is recommended you use it for all values out of an abundance of caution and for consistency. + +##### Example + +```json +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/api/<.*>", + "methods": ["GET"] + }, + "authenticators": [/* ... */], + "authorizer": {/* ... */} + "credentials_issuer" { + "handler": "headers", + "config": { + "headers": { + "X-User": "{{ print .Subject }}", + "X-Some-Arbitrary-Data": "{{ print .Extra.some.arbitrary.data }}" + } + } + } +} +``` + +#### `cookies` + +This credentials issuer will transform the request, allowing you to pass the credentials to the upstream application via +the cookies. + +The cookies are specified via the `cookies` field of the credentials issuers `config` field. The keys are the cookie name +and the values are a string which will be parsed by the Go [`text/template`](https://golang.org/pkg/text/template/) package +for value substitution, receiving the [AuthenticationSession](https://github.com/ory/oathkeeper/blob/92c09fb28552949cd034ed5555c87dfda91407a3/proxy/authenticator.go#L19) struct: + +```go +type AuthenticationSession struct { + Subject string + Extra map[string]interface{} +} +``` + +Note that the `AuthenticationSession` struct has a field name `Extra` which is a `map[string]interface{}`, which receives +varying introspection data from the authentication process. Because the contents of `Extra` are so variable, nested and +potentially non-existent values need special handling by the `text/template` parser, and a `print` FuncMap function has +been provided to ensure the non-existent map values will simply return an empty string, rather than ``. + +If you find that your cookies contain the string `` then you have most likely omitted the `print` function, and +it is recommended you use it for all values out of an abundance of caution and for consistency. + +##### Example + +```json +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://my-app/api/<.*>", + "methods": ["GET"] + }, + "authenticators": [/* ... */], + "authorizer": {/* ... */} + "credentials_issuer" { + "handler": "cookies", + "config": { + "cookies": { + "user": "{{ print .Subject }}", + "some-arbitrary-data": "{{ print .Extra.some.arbitrary.data }}" + } + } + } +} +``` + +## Scope Strategies + +The following scope strategies are supported: + +* `HIERARCHIC`: Scope `foo` matches `foo`, `foo.bar`, `foo.baz` but not `bar` +* `WILDCARD`: Scope `foo.*` matches `foo`, `foo.bar`, `foo.baz` but not `bar`. Scope `foo` matches `foo` but not `foo.bar` nor `bar` +* `EXACT`: Scope `foo` matches `foo` but not `bar` nor `foo.bar` +* `NONE`: Scope validation is disabled completely. It is expected that the upstream logic (e.g. OAuth 2.0 Token Introspection) handles scope validation +properly. If no upstream logic (e.g. JWT) exists, an error will be thrown if the scope is to be validated. + +## Rule Management + +### Rules REST API + +For more information on available fields and exemplary payloads of rules, as well as rule management using HTTP +please refer to the [REST API docs](https://www.ory.sh/docs/api/oathkeeper/) + +### Rules CLI API + +Management of rules is not only possible through the REST API, but additionally using the ORY Oathkeeper CLI. +For help on how to manage the CLI, type `oathkeeper help rules`. diff --git a/docs/oathkeeper/configure-deploy.md b/docs/oathkeeper/configure-deploy.md new file mode 100644 index 000000000..fd6bf458e --- /dev/null +++ b/docs/oathkeeper/configure-deploy.md @@ -0,0 +1,161 @@ +--- +id: oathkeeper-configure-deploy +title: Configure and Deploy +--- + +ORY Oathkeeper has two servers that run on separate ports: + +* The api server: This server is responsible for exposing the management REST API. +* The proxy server: This server is responsible for evaluating access requests and forwarding them to the backend. + +For detailed documentation on the two servers, run `oathkeeper help serve api` and `oathkeeper help serve proxy`. + +ORY Oathkeeper supports two types of storage adapters: + +* In-memory: This adapter does not work with more than one instance ("cluster") and any state is lost after restarting the instance. +* SQL: This adapter works with more than one instance and state is not lost after restarts. + +The SQL adapter supports two DBMS: PostgreSQL 9.6+ and MySQL 5.7+. Please note that +older MySQL versions have issues with the database schema. +For more information [go here](https://github.com/ory/hydra/issues/377). + +ORY Oathkeeper supports various authentication, authorization, and credential strategies. Depending on what strategies +you want to use, you will have to configure more services (e.g. ORY Hydra or ORY Keto). In this tutorial, we will +set up ORY Oathkeeper without any of the other services. Please refer to the [authenticator, authorizer, and credentials +issuer documentation](./api-access-rules.md) to see what you need to configure in order to get the strategies you need. + +This guide will: + +1. Download and run a PostgreSQL container in Docker. +2. Download and run ORY Oathkeeper using Docker. + +## Installation + +You can install ORY Oathkeeper by downloading the [binaries](https://github.com/ory/oathkeeper/releases), by using +the precompiled Docker Image available at [Docker Hub](https://hub.docker.com/r/oryd/oathkeeper/), or by +compiling the code yourself. + +### Docker Hub + +The recommended way to install and run ORY Oathkeeper is via docker: + +```sh +$ docker run oryd/oathkeeper: help +``` + +### Without Docker + +#### Binaries + +If you [download the binaries](https://github.com/ory/oathkeeper/releases), make sure +to add them to your path (e.g. `/usr/bin`). Then, run `oathkeeper help` + +#### From Source + +To install ORY Oathkeeper from source, you need to have Go 1.11+ installed. +Then, run: + +``` +go get -d -u github.com/ory/oathkeeper +cd $(go env GOPATH)/src/github.com/ory/oathkeeper +OATHKEEPER_LATEST=$(git describe --abbrev=0 --tags) +git checkout $OATHKEEPER_LATEST +GO111MODULE=on go install \ + -ldflags "-X github.com/ory/oathkeeper/cmd.Version=$OATHKEEPER_LATEST -X github.com/ory/oathkeeper/cmd.BuildTime=`TZ=UTC date -u '+%Y-%m-%dT%H:%M:%SZ'` -X github.com/ory/oathkeeper/cmd.GitHash=`git rev-parse HEAD`" \ + github.com/ory/oathkeeper +git checkout master +oathkeeper help +``` + +## Create a Network + +Before we can start, a network must be created which we will attach all our Docker containers to. That way, the containers +can talk to one another. + +``` +$ docker network create oathkeeperguide +``` + +## Start the PostgreSQL Container + +For the purpose of this tutorial, we will use PostgreSQL as a database. As you probably already know, don't run databases in Docker in production! +For the sake of this tutorial however, let's use Docker to quickly deploy the database. + +``` +$ docker run \ + --network oathkeeperguide \ + --name ory-oathkeeper-example--postgres \ + -e POSTGRES_USER=oathkeeper \ + -e POSTGRES_PASSWORD=secret \ + -e POSTGRES_DB=oathkeeper \ + -d postgres:9.6 +``` + +This command wil start a postgres instance with name `ory-oathkeeper-example--postgres`, set up a database called `oathkeeper` +and create a user `oathkeeper` with password `secret`. + +## Run the ORY Oathkeeper API Proxy + +``` +# The database url points us at the postgres instance. This could also be an ephermal in-memory database (`export DATABASE_URL=memory`) +# or a MySQL URI. +$ export DATABASE_URL=postgres://oathkeeper:secret@ory-oathkeeper-example--postgres:5432/oathkeeper?sslmode=disable + +# This pulls the latest image from Docker Hub +$ docker pull oryd/oathkeeper:v0.13.5_oryOS.5 + +# ORY Oathkeeper does not do magic, it requires conscious decisions, for example running SQL migrations which is required +# when installing a new version of ORY Oathkeeper, or upgrading an existing installation. +# It is the equivalent to `oathkeeper migrate sql postgres://oathkeeper:secret@ory-oathkeeper-example--postgres:5432/oathkeeper?sslmode=disable` +$ docker run -it --rm \ + --network oathkeeperguide \ + oryd/oathkeeper:v0.13.5_oryOS.5 \ + migrate sql $DATABASE_URL + +Applying `client` SQL migrations... +[...] +Migration successful! + +# Next, let's run the API server! +# +# Please make sure to use your own secret. +$ docker run -d \ + --name ory-oathkeeper-example--oathkeeper-api \ + --network oathkeeperguide \ + -p 4456:4456 \ + -e DATABASE_URL=$DATABASE_URL \ + -e PORT=4456 \ + -e CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET=changemechangemechangemechangemedo \ + oryd/oathkeeper:v0.13.5_oryOS.5 \ + serve api + +# And the proxy server too - take not that we need to link the proxy serve with the API server! +# +# Please make sure to use your own secret. +$ docker run -d \ + --name ory-oathkeeper-example--oathkeeper-proxy \ + --network oathkeeperguide \ + -p 4455:4455 \ + -e OATHKEEPER_API_URL=http://ory-oathkeeper-example--oathkeeper-api:4456/ \ + -e PORT=4455 \ + -e CREDENTIALS_ISSUER_ID_TOKEN_HS256_SECRET=changemechangemechangemechangeme \ + oryd/oathkeeper:v0.13.5_oryOS.5 \ + serve proxy +``` + +Great, both the API and the proxy server are running now! Make sure to check the logs and see if there were +any errors or issues before going to the next steps: + +``` +$ docker logs ory-oathkeeper-example--oathkeeper-api +$ docker logs ory-oathkeeper-example--oathkeeper-proxy +``` + +### Creating your first access rule + +**Sorry, this section is still work in progress.** + +1. Create the rule using `oathkeeper rules import` - need to figure out how to add the file to docker to make this work. +2. Have a rule that works immediately, e.g. protect an Oathkeeper API URL using the Oathkeeper proxy +3. Have two CURL requests where one fails and one passes. Explain why that happens +4. Clean up diff --git a/docs/oathkeeper/control-flow.md b/docs/oathkeeper/control-flow.md new file mode 100644 index 000000000..545c248bb --- /dev/null +++ b/docs/oathkeeper/control-flow.md @@ -0,0 +1,99 @@ +--- +id: oathkeeper-control-flow +title: Control Flow +--- + +ORY Oathkeeper has two methods of answering access requests: proxy and rest. + +## Proxy + +The proxy process (`oathkeeper serve proxy`) forwards requests to the upstream server, defined in the rule, +if the request is allowed. If the request is not allowed, ORY Oathkeeper does not forward the request and instead +returns an error message. + +Assuming you are making the following request + +``` +GET /my-service/whatever HTTP/1.1 +Host: oathkeeper-proxy:4455 +Authorization: bearer some-token +``` + +and you have the following rule defined (which allows this request) + +``` +{ + "id": "some-id", + "upstream": { + "url": "http://my-backend-service" + }, + "match": { + "url": "http://oathkeeper-proxy:4455/my-service/whatever", + "methods": [ "GET" ] + }, + "authenticators": [{ "handler": "noop" }], + "authorizer": { "handler": "allow" } + "credentials_issuer": { "handler": "noop" } +} +``` + +then the request will be forwarded by ORY Oathkeeper as follows: + + +``` +GET /my-service/whatever HTTP/1.1 +Host: my-backend-service:4455 +Authorization: bearer some-token +``` + +The response of this request will then be sent to the client that made the request to ORY Oathkeeper. + +## REST + +The `/judge` endpoint served by `oathkeeper serve api` works in a similar fashion to the proxy, with the difference +of not forwarding the request to the upstream server but instead returning a response which indicates if the request +should be allowed to proceed or not. + +Assuming you are making the following request + +``` +GET /judge/my-service/whatever HTTP/1.1 +Host: oathkeeper-api:4456 +Authorization: bearer some-token +``` + +and you have the following rule defined (which allows this request) + +``` +{ + "id": "some-id", + "upstream": { "url": "" }, + "match": { + "url": "http://oathkeeper-api:4456/my-service/whatever", + "methods": [ "GET" ] + }, + "authenticators": [{ "handler": "noop" }], + "authorizer": { "handler": "allow" } + "credentials_issuer": { "handler": "noop" } +} +``` + +then this endpoint will directly respond with HTTP Status Code 200: + + +``` +HTTP/1.1 200 OK +Authorization: bearer-sometoken +``` + +If any other status code is returned, the request must not be allowed, for example: + +``` +HTTP/1.1 401 OK +Content-Length: 0 +Connection: Closed +``` + +The judge endpoint automatically strips the `/judge` path prefix from the request URL so you don't need to add that +prefix to the `match.url` value of your rule (use `http://oathkeeper-api:4456/my-service/whatever` +instead of `http://oathkeeper-api:4456/judge/my-service/whatever`). diff --git a/docs/oathkeeper/index.md b/docs/oathkeeper/index.md new file mode 100644 index 000000000..8e66cb552 --- /dev/null +++ b/docs/oathkeeper/index.md @@ -0,0 +1,13 @@ +--- +id: oathkeeper-index +title: Introduction +--- + +Welcome to the ORY Oathkeeper documentation! + +ORY Oathkeeper is a reverse proxy which evaluates incoming HTTP requests based on a set of rules (called "access rules"). +The feature set ORY Oathkeeper implements is referred to as an Identity and Access Proxy (IAP) in the context +of [BeyondCorp and ZeroTrust](https://www.beyondcorp.com). + +In principal, ORY Oathkeeper inspects the `Authorization` header and the full request url (e.g. `https://mydomain.com/api/foo`) +of incoming HTTP requests, applies a set of rules, and either grants access to the requested url or denies access. diff --git a/docs/oathkeeper/sdk/api.md b/docs/oathkeeper/sdk/api.md new file mode 100644 index 000000000..cdac8fa6f --- /dev/null +++ b/docs/oathkeeper/sdk/api.md @@ -0,0 +1,2925 @@ +--- +title: REST API +id: oathkeeper-sdk-api +--- + + + +ORY Oathkeeper is a reverse proxy that checks the HTTP Authorization for validity against a set of rules. This service uses Hydra to validate access tokens and policies. + +> You are viewing a REST API documentation. This documentation is auto-generated from a swagger specification which +itself is generated from annotations in the source files of the project. It is possible that this documentation includes +bugs and that code samples are incomplete or wrong. +> +> If you find issues in the respective documentation, please do not edit the +markdown files directly (as they are generated) but raise an issue on the project's GitHub instead. This documentation +will improve over time with your help! If you have ideas how to improve this part of the documentation, feel free to +share them in a [GitHub issue](https://github.com/ory/docs/issues/new) any time. + + +## Default + + + +### Returns well known keys + +``` +GET /.well-known/jwks.json HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns public keys for validating the ID tokens issued by ORY Oathkeeper. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|jsonWebKeySet|[jsonWebKeySet](#schemajsonwebkeyset)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "keys": [ + { + "alg": "string", + "crv": "string", + "d": "string", + "dp": "string", + "dq": "string", + "e": "string", + "k": "string", + "kid": "string", + "kty": "string", + "n": "string", + "p": "string", + "q": "string", + "qi": "string", + "use": "string", + "x": "string", + "x5c": [ + "string" + ], + "y": "string" + } + ] +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /.well-known/jwks.json \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/.well-known/jwks.json", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/.well-known/jwks.json', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/.well-known/jwks.json"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/.well-known/jwks.json', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/.well-known/jwks.json', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## health + + + +### Check the Alive Status + +``` +GET /health/alive HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns a 200 status code when the HTTP server is up running. +This status does currently not include checks whether the database connection is working. +This endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set. + +Be aware that if you are running multiple nodes of ORY Oathkeeper, the health status will never refer to the cluster state, only to a single instance. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|healthStatus|[healthStatus](#schemahealthstatus)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /health/alive \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/health/alive", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/health/alive', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/health/alive"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/health/alive', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/health/alive', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Check the Readiness Status + +``` +GET /health/ready HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns a 200 status code when the HTTP server is up running and the environment dependencies (e.g. +the database) are responsive as well. + +This status does currently not include checks whether the database connection is working. +This endpoint does not require the `X-Forwarded-Proto` header when TLS termination is set. + +Be aware that if you are running multiple nodes of ORY Oathkeeper, the health status will never refer to the cluster state, only to a single instance. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|healthStatus|[healthStatus](#schemahealthstatus)| +|503|[Service Unavailable](https://tools.ietf.org/html/rfc7231#section-6.6.4)|healthNotReadyStatus|[healthNotReadyStatus](#schemahealthnotreadystatus)| + +##### Examples + +###### 200 response + +```json +{ + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /health/ready \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/health/ready", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/health/ready', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/health/ready"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/health/ready', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/health/ready', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## judge + + + +### Judge if a request should be allowed or not + +``` +GET /judge HTTP/1.1 +Accept: application/json + +``` + +This endpoint mirrors the proxy capability of ORY Oathkeeper's proxy functionality but instead of forwarding the +request to the upstream server, returns 200 (request should be allowed), 401 (unauthorized), or 403 (forbidden) +status codes. This endpoint can be used to integrate with other API Proxies like Ambassador, Kong, Envoy, and many more. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **404** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /judge \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/judge", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/judge', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/judge"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/judge', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/judge', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## rule + + + +### List all rules + +``` +GET /rules HTTP/1.1 +Accept: application/json + +``` + +This method returns an array of all rules that are stored in the backend. This is useful if you want to get a full +view of what rules you have currently in place. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|limit|query|integer(int64)|false|The maximum amount of rules returned.| +|offset|query|integer(int64)|false|The offset from where to start looking.| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A list of rules|Inline| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[[rule](#schemarule)]|false|none|none| +|» swaggerRule is a single rule that will get checked on every HTTP request.|[rule](#schemarule)|false|none|none| +|»» authenticators|[[ruleHandler](#schemarulehandler)]|false|none|Authenticators is a list of authentication handlers that will try and authenticate the provided credentials. Authenticators are checked iteratively from index 0 to n and if the first authenticator to return a positive result will be the one used. If you want the rule to first check a specific authenticator before "falling back" to others, have that authenticator as the first item in the array.| +|»»» config|string|false|none|Config contains the configuration for the handler. Please read the user guide for a complete list of each handler's available settings.| +|»»» handler|string|false|none|Handler identifies the implementation which will be used to handle this specific request. Please read the user guide for a complete list of available handlers.| +|»» authorizer|[ruleHandler](#schemarulehandler)|false|none|none| +|»» credentials_issuer|[ruleHandler](#schemarulehandler)|false|none|none| +|»» description|string|false|none|Description is a human readable description of this rule.| +|»» id|string|false|none|ID is the unique id of the rule. It can be at most 190 characters long, but the layout of the ID is up to you. You will need this ID later on to update or delete the rule.| +|»» match|[ruleMatch](#schemarulematch)|false|none|none| +|»»» methods|[string]|false|none|An array of HTTP methods (e.g. GET, POST, PUT, DELETE, ...). When ORY Oathkeeper searches for rules to decide what to do with an incoming request to the proxy server, it compares the HTTP method of the incoming request with the HTTP methods of each rules. If a match is found, the rule is considered a partial match. If the matchesUrl field is satisfied as well, the rule is considered a full match.| +|»»» url|string|false|none|This field represents the URL pattern this rule matches. When ORY Oathkeeper searches for rules to decide what to do with an incoming request to the proxy server, it compares the full request URL (e.g. https://mydomain.com/api/resource) without query parameters of the incoming request with this field. If a match is found, the rule is considered a partial match. If the matchesMethods field is satisfied as well, the rule is considered a full match. You can use regular expressions in this field to match more than one url. Regular expressions are encapsulated in brackets < and >. The following example matches all paths of the domain `mydomain.com`: `https://mydomain.com/<.*>`. For more information refer to: https://ory.gitbooks.io/oathkeeper/content/concepts.html#rules| +|»» upstream|[Upstream](#schemaupstream)|false|none|none| +|»»» preserve_host|boolean|false|none|PreserveHost, if false (the default), tells ORY Oathkeeper to set the upstream request's Host header to the hostname of the API's upstream's URL. Setting this flag to true instructs ORY Oathkeeper not to do so.| +|»»» strip_path|string|false|none|StripPath if set, replaces the provided path prefix when forwarding the requested URL to the upstream URL.| +|»»» url|string|false|none|URL is the URL the request will be proxied to.| + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +[ + { + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } + } +] +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /rules \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/rules", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/rules', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/rules"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/rules', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/rules', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Create a rule + +``` +POST /rules HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +This method allows creation of rules. If a rule id exists, you will receive an error. + +#### Request body + +```json +{ + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[rule](#schemarule)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|A rule|[rule](#schemarule)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 201 response + +```json +{ + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X POST /rules \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("POST", "/rules", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/rules', { + method: 'POST', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/rules"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("POST"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.post( + '/rules', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.post '/rules', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Retrieve a rule + +``` +GET /rules/{id} HTTP/1.1 +Accept: application/json + +``` + +Use this method to retrieve a rule from the storage. If it does not exist you will receive a 404 error. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A rule|[rule](#schemarule)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **404** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /rules/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/rules/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/rules/{id}', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/rules/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/rules/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/rules/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Update a rule + +``` +PUT /rules/{id} HTTP/1.1 +Content-Type: application/json +Accept: application/json + +``` + +Use this method to update a rule. Keep in mind that you need to send the full rule payload as this endpoint does +not support patching. + +#### Request body + +```json +{ + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } +} +``` + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|none| +|body|body|[rule](#schemarule)|false|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|A rule|[rule](#schemarule)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **404** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 200 response + +```json +{ + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X PUT /rules/{id} \ + -H 'Content-Type: application/json' \ -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Content-Type": []string{"application/json"}, + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("PUT", "/rules/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); +const input = '{ + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } +}'; +const headers = { + 'Content-Type': 'application/json', 'Accept': 'application/json' +} + +fetch('/rules/{id}', { + method: 'PUT', + body: input, + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/rules/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("PUT"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' +} + +r = requests.put( + '/rules/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' +} + +result = RestClient.put '/rules/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + + +### Delete a rule + +``` +DELETE /rules/{id} HTTP/1.1 +Accept: application/json + +``` + +Use this endpoint to delete a rule. + + +##### Parameters + +|Parameter|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|none| + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|An empty response|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|The standard error format|Inline| +|403|[Forbidden](https://tools.ietf.org/html/rfc7231#section-6.5.3)|The standard error format|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|The standard error format|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|The standard error format|Inline| + + +##### Response Schema + +Status Code **401** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **403** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **404** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +Status Code **500** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|» code|integer(int64)|false|none|none| +|» details|[object]|false|none|none| +|»» **additionalProperties**|object|false|none|none| +|» message|string|false|none|none| +|» reason|string|false|none|none| +|» request|string|false|none|none| +|» status|string|false|none|none| + +##### Examples + +###### 401 response + +```json +{ + "code": 0, + "details": [ + { + "property1": {}, + "property2": {} + } + ], + "message": "string", + "reason": "string", + "request": "string", + "status": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X DELETE /rules/{id} \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("DELETE", "/rules/{id}", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/rules/{id}', { + method: 'DELETE', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/rules/{id}"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("DELETE"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.delete( + '/rules/{id}', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.delete '/rules/{id}', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ + +## version + + + +### Get the version of Oathkeeper + +``` +GET /version HTTP/1.1 +Accept: application/json + +``` + +This endpoint returns the version as `{ "version": "VERSION" }`. The version is only correct with the prebuilt binary and not custom builds. + +#### Responses + + +##### Overview + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|version|[version](#schemaversion)| + +##### Examples + +###### 200 response + +```json +{ + "version": "string" +} +``` + + + +#### Code samples + +
+ +
+
+ +```shell +curl -X GET /version \ + -H 'Accept: application/json' +``` + +
+
+ +```go +package main + +import ( + "bytes" + "net/http" +) + +func main() { + headers := map[string][]string{ + "Accept": []string{"application/json"}, + } + + var body []byte + // body = ... + + req, err := http.NewRequest("GET", "/version", bytes.NewBuffer(body)) + req.Header = headers + + client := &http.Client{} + resp, err := client.Do(req) + // ... +} +``` + +
+
+ +```nodejs +const fetch = require('node-fetch'); + +const headers = { + 'Accept': 'application/json' +} + +fetch('/version', { + method: 'GET', + headers +}) +.then(r => r.json()) +.then((body) => { + console.log(body) +}) +``` + +
+
+ +```java +// This sample needs improvement. +URL obj = new URL("/version"); + +HttpURLConnection con = (HttpURLConnection) obj.openConnection(); +con.setRequestMethod("GET"); + +int responseCode = con.getResponseCode(); + +BufferedReader in = new BufferedReader( + new InputStreamReader(con.getInputStream()) +); + +String inputLine; +StringBuffer response = new StringBuffer(); +while ((inputLine = in.readLine()) != null) { + response.append(inputLine); +} +in.close(); + +System.out.println(response.toString()); +``` + +
+
+ +```python +import requests + +headers = { + 'Accept': 'application/json' +} + +r = requests.get( + '/version', + params={}, + headers = headers) + +print r.json() +``` + +
+
+ +```ruby +require 'rest-client' +require 'json' + +headers = { + 'Accept' => 'application/json' +} + +result = RestClient.get '/version', + params: {}, headers: headers + +p JSON.parse(result) +``` + +
+
+
+ +## Schemas + +Upstream +#### Upstream + + + +```json +{ + "preserve_host": true, + "strip_path": "string", + "url": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|preserve_host|boolean|false|none|PreserveHost, if false (the default), tells ORY Oathkeeper to set the upstream request's Host header to the hostname of the API's upstream's URL. Setting this flag to true instructs ORY Oathkeeper not to do so.| +|strip_path|string|false|none|StripPath if set, replaces the provided path prefix when forwarding the requested URL to the upstream URL.| +|url|string|false|none|URL is the URL the request will be proxied to.| + +healthNotReadyStatus +#### healthNotReadyStatus + + + +```json +{ + "errors": { + "property1": "string", + "property2": "string" + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|errors|object|false|none|Errors contains a list of errors that caused the not ready status.| +|» **additionalProperties**|string|false|none|none| + +healthStatus +#### healthStatus + + + +```json +{ + "status": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|Status always contains "ok".| + +jsonWebKey +#### jsonWebKey + + + +```json +{ + "alg": "string", + "crv": "string", + "d": "string", + "dp": "string", + "dq": "string", + "e": "string", + "k": "string", + "kid": "string", + "kty": "string", + "n": "string", + "p": "string", + "q": "string", + "qi": "string", + "use": "string", + "x": "string", + "x5c": [ + "string" + ], + "y": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|alg|string|false|none|The "alg" (algorithm) parameter identifies the algorithm intended for use with the key. The values used should either be registered in the IANA "JSON Web Signature and Encryption Algorithms" registry established by [JWA] or be a value that contains a Collision- Resistant Name.| +|crv|string|false|none|none| +|d|string|false|none|none| +|dp|string|false|none|none| +|dq|string|false|none|none| +|e|string|false|none|none| +|k|string|false|none|none| +|kid|string|false|none|The "kid" (key ID) parameter is used to match a specific key. This is used, for instance, to choose among a set of keys within a JWK Set during key rollover. The structure of the "kid" value is unspecified. When "kid" values are used within a JWK Set, different keys within the JWK Set SHOULD use distinct "kid" values. (One example in which different keys might use the same "kid" value is if they have different "kty" (key type) values but are considered to be equivalent alternatives by the application using them.) The "kid" value is a case-sensitive string.| +|kty|string|false|none|The "kty" (key type) parameter identifies the cryptographic algorithm family used with the key, such as "RSA" or "EC". "kty" values should either be registered in the IANA "JSON Web Key Types" registry established by [JWA] or be a value that contains a Collision- Resistant Name. The "kty" value is a case-sensitive string.| +|n|string|false|none|none| +|p|string|false|none|none| +|q|string|false|none|none| +|qi|string|false|none|none| +|use|string|false|none|The "use" (public key use) parameter identifies the intended use of the public key. The "use" parameter is employed to indicate whether a public key is used for encrypting data or verifying the signature on data. Values are commonly "sig" (signature) or "enc" (encryption).| +|x|string|false|none|none| +|x5c|[string]|false|none|The "x5c" (X.509 certificate chain) parameter contains a chain of one or more PKIX certificates [RFC5280]. The certificate chain is represented as a JSON array of certificate value strings. Each string in the array is a base64-encoded (Section 4 of [RFC4648] -- not base64url-encoded) DER [ITU.X690.1994] PKIX certificate value. The PKIX certificate containing the key value MUST be the first certificate.| +|y|string|false|none|none| + +jsonWebKeySet +#### jsonWebKeySet + + + +```json +{ + "keys": [ + { + "alg": "string", + "crv": "string", + "d": "string", + "dp": "string", + "dq": "string", + "e": "string", + "k": "string", + "kid": "string", + "kty": "string", + "n": "string", + "p": "string", + "q": "string", + "qi": "string", + "use": "string", + "x": "string", + "x5c": [ + "string" + ], + "y": "string" + } + ] +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|keys|[[jsonWebKey](#schemajsonwebkey)]|false|none|The value of the "keys" parameter is an array of JWK values. By default, the order of the JWK values within the array does not imply an order of preference among them, although applications of JWK Sets can choose to assign a meaning to the order for their purposes, if desired.| + +rule +#### rule + + + +```json +{ + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } +} + +``` + +*swaggerRule is a single rule that will get checked on every HTTP request.* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|authenticators|[[ruleHandler](#schemarulehandler)]|false|none|Authenticators is a list of authentication handlers that will try and authenticate the provided credentials. Authenticators are checked iteratively from index 0 to n and if the first authenticator to return a positive result will be the one used. If you want the rule to first check a specific authenticator before "falling back" to others, have that authenticator as the first item in the array.| +|authorizer|[ruleHandler](#schemarulehandler)|false|none|none| +|credentials_issuer|[ruleHandler](#schemarulehandler)|false|none|none| +|description|string|false|none|Description is a human readable description of this rule.| +|id|string|false|none|ID is the unique id of the rule. It can be at most 190 characters long, but the layout of the ID is up to you. You will need this ID later on to update or delete the rule.| +|match|[ruleMatch](#schemarulematch)|false|none|none| +|upstream|[Upstream](#schemaupstream)|false|none|none| + +ruleHandler +#### ruleHandler + + + +```json +{ + "config": "string", + "handler": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|config|string|false|none|Config contains the configuration for the handler. Please read the user guide for a complete list of each handler's available settings.| +|handler|string|false|none|Handler identifies the implementation which will be used to handle this specific request. Please read the user guide for a complete list of available handlers.| + +ruleMatch +#### ruleMatch + + + +```json +{ + "methods": [ + "string" + ], + "url": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|methods|[string]|false|none|An array of HTTP methods (e.g. GET, POST, PUT, DELETE, ...). When ORY Oathkeeper searches for rules to decide what to do with an incoming request to the proxy server, it compares the HTTP method of the incoming request with the HTTP methods of each rules. If a match is found, the rule is considered a partial match. If the matchesUrl field is satisfied as well, the rule is considered a full match.| +|url|string|false|none|This field represents the URL pattern this rule matches. When ORY Oathkeeper searches for rules to decide what to do with an incoming request to the proxy server, it compares the full request URL (e.g. https://mydomain.com/api/resource) without query parameters of the incoming request with this field. If a match is found, the rule is considered a partial match. If the matchesMethods field is satisfied as well, the rule is considered a full match. You can use regular expressions in this field to match more than one url. Regular expressions are encapsulated in brackets < and >. The following example matches all paths of the domain `mydomain.com`: `https://mydomain.com/<.*>`. For more information refer to: https://ory.gitbooks.io/oathkeeper/content/concepts.html#rules| + +swaggerCreateRuleParameters +#### swaggerCreateRuleParameters + + + +```json +{ + "Body": { + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } + } +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[rule](#schemarule)|false|none|none| + +swaggerGetRuleParameters +#### swaggerGetRuleParameters + + + +```json +{ + "id": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|in: path| + +swaggerListRulesParameters +#### swaggerListRulesParameters + + + +```json +{ + "limit": 0, + "offset": 0 +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|limit|integer(int64)|false|none|The maximum amount of rules returned. in: query| +|offset|integer(int64)|false|none|The offset from where to start looking. in: query| + +swaggerRuleResponse +#### swaggerRuleResponse + + + +```json +{ + "Body": { + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } + } +} + +``` + +*A rule* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[rule](#schemarule)|false|none|none| + +swaggerRulesResponse +#### swaggerRulesResponse + + + +```json +{ + "Body": [ + { + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } + } + ] +} + +``` + +*A list of rules* + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[[rule](#schemarule)]|false|none|in: body type: array| + +swaggerUpdateRuleParameters +#### swaggerUpdateRuleParameters + + + +```json +{ + "Body": { + "authenticators": [ + { + "config": "string", + "handler": "string" + } + ], + "authorizer": { + "config": "string", + "handler": "string" + }, + "credentials_issuer": { + "config": "string", + "handler": "string" + }, + "description": "string", + "id": "string", + "match": { + "methods": [ + "string" + ], + "url": "string" + }, + "upstream": { + "preserve_host": true, + "strip_path": "string", + "url": "string" + } + }, + "id": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|Body|[rule](#schemarule)|false|none|none| +|id|string|true|none|in: path| + +version +#### version + + + +```json +{ + "version": "string" +} + +``` + +#### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|version|string|false|none|none| + diff --git a/docs/performance/hydra.md b/docs/performance/hydra.md new file mode 100644 index 000000000..7969e6c7c --- /dev/null +++ b/docs/performance/hydra.md @@ -0,0 +1,247 @@ +--- +id: performance-hydra +title: ORY Hydra +--- + +In this document you will find benchmark results for different endpoints of ORY Hydra. All benchmarks are executed +using [rakyll/hey](https://github.com/rakyll/hey). Please note that these benchmarks run against the in-memory storage +adapter of ORY Hydra. These benchmarks represent what performance you would get with a zero-overhead database implementation. + +We do not include benchmarks against databases (e.g. MySQL or PostgreSQL) as the performance greatly differs between +deployments (e.g. request latency, database configuration) and tweaking individual things may greatly improve performance. +We believe, for that reason, that benchmark results for these database adapters are difficult to generalize and potentially +deceiving. They are thus not included. + +This file is updated on every push to master. It thus represents the benchmark data for the latest version. + +All benchmarks run 10.000 requests in total, with 100 concurrent requests. All benchmarks run on Circle-CI with a +["2 CPU cores and 4GB RAM"](https://support.circleci.com/hc/en-us/articles/360000489307-Why-do-my-tests-take-longer-to-run-on-CircleCI-than-locally-) +configuration. + +## BCrypt + +ORY Hydra uses BCrypt to obfuscate secrets of OAuth 2.0 Clients. When using flows such as the OAuth 2.0 Client Credentials +Grant, ORY Hydra validates the client credentials using BCrypt which causes (by design) CPU load. CPU load and performance +depend on the BCrypt cost which can be set using the environment variable `BCRYPT_COST`. For these benchmarks, +we have set `BCRYPT_COST=8`. + +## OAuth 2.0 + +This section contains various benchmarks against OAuth 2.0 endpoints + +### Token Introspection + +``` + +Summary: + Total: 0.4787 secs + Slowest: 0.0237 secs + Fastest: 0.0001 secs + Average: 0.0046 secs + Requests/sec: 20888.5191 + + Total data: 1550000 bytes + Size/request: 155 bytes + +Response time histogram: + 0.000 [1] | + 0.002 [3477] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.005 [1585] |■■■■■■■■■■■■■■■■■■ + 0.007 [2230] |■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.010 [2043] |■■■■■■■■■■■■■■■■■■■■■■■■ + 0.012 [524] |■■■■■■ + 0.014 [79] |■ + 0.017 [18] | + 0.019 [16] | + 0.021 [15] | + 0.024 [12] | + + +Latency distribution: + 10% in 0.0001 secs + 25% in 0.0003 secs + 50% in 0.0047 secs + 75% in 0.0074 secs + 90% in 0.0088 secs + 95% in 0.0101 secs + 99% in 0.0124 secs + +Details (average, fastest, slowest): + DNS+dialup: 0.0000 secs, 0.0001 secs, 0.0237 secs + DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0050 secs + req write: 0.0000 secs, 0.0000 secs, 0.0066 secs + resp wait: 0.0044 secs, 0.0001 secs, 0.0159 secs + resp read: 0.0000 secs, 0.0000 secs, 0.0045 secs + +Status code distribution: + [200] 10000 responses + + + +``` + +### Client Credentials Grant + +This endpoint uses [BCrypt](#bcrypt). + +``` + +Summary: + Total: 18.4227 secs + Slowest: 0.9163 secs + Fastest: 0.0165 secs + Average: 0.1758 secs + Requests/sec: 542.8074 + + Total data: 1570000 bytes + Size/request: 157 bytes + +Response time histogram: + 0.016 [1] | + 0.106 [3034] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.196 [3276] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.286 [2161] |■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.376 [932] |■■■■■■■■■■■ + 0.466 [409] |■■■■■ + 0.556 [115] |■ + 0.646 [50] |■ + 0.736 [13] | + 0.826 [7] | + 0.916 [2] | + + +Latency distribution: + 10% in 0.0790 secs + 25% in 0.0997 secs + 50% in 0.1766 secs + 75% in 0.2145 secs + 90% in 0.3029 secs + 95% in 0.3880 secs + 99% in 0.5007 secs + +Details (average, fastest, slowest): + DNS+dialup: 0.0001 secs, 0.0165 secs, 0.9163 secs + DNS-lookup: 0.0001 secs, 0.0000 secs, 0.0228 secs + req write: 0.0002 secs, 0.0000 secs, 0.0826 secs + resp wait: 0.1748 secs, 0.0165 secs, 0.9162 secs + resp read: 0.0006 secs, 0.0000 secs, 0.0845 secs + +Status code distribution: + [200] 10000 responses + + + +``` + +## OAuth 2.0 Client Management + +### Creating OAuth 2.0 Clients + +This endpoint uses [BCrypt](#bcrypt) and generates IDs and secrets by reading from which negatively impacts +performance. Performance will be better if IDs and secrets are set in the request as opposed to generated by ORY Hydra. + +``` +This test is currently disabled due to issues with /dev/urandom being inaccessible in the CI. +``` + +### Listing OAuth 2.0 Clients + +``` + +Summary: + Total: 0.3261 secs + Slowest: 0.0272 secs + Fastest: 0.0001 secs + Average: 0.0030 secs + Requests/sec: 30663.5373 + + Total data: 4370000 bytes + Size/request: 437 bytes + +Response time histogram: + 0.000 [1] | + 0.003 [5919] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.005 [1790] |■■■■■■■■■■■■ + 0.008 [1158] |■■■■■■■■ + 0.011 [752] |■■■■■ + 0.014 [268] |■■ + 0.016 [86] |■ + 0.019 [20] | + 0.022 [3] | + 0.024 [2] | + 0.027 [1] | + + +Latency distribution: + 10% in 0.0001 secs + 25% in 0.0002 secs + 50% in 0.0010 secs + 75% in 0.0052 secs + 90% in 0.0085 secs + 95% in 0.0103 secs + 99% in 0.0139 secs + +Details (average, fastest, slowest): + DNS+dialup: 0.0000 secs, 0.0001 secs, 0.0272 secs + DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0033 secs + req write: 0.0000 secs, 0.0000 secs, 0.0061 secs + resp wait: 0.0028 secs, 0.0000 secs, 0.0220 secs + resp read: 0.0001 secs, 0.0000 secs, 0.0094 secs + +Status code distribution: + [200] 10000 responses + + + +``` + +### Fetching a specific OAuth 2.0 Client + +``` + +Summary: + Total: 0.3242 secs + Slowest: 0.0220 secs + Fastest: 0.0001 secs + Average: 0.0030 secs + Requests/sec: 30844.1584 + + Total data: 4350000 bytes + Size/request: 435 bytes + +Response time histogram: + 0.000 [1] | + 0.002 [5608] |■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ + 0.004 [1402] |■■■■■■■■■■ + 0.007 [1235] |■■■■■■■■■ + 0.009 [888] |■■■■■■ + 0.011 [505] |■■■■ + 0.013 [203] |■ + 0.015 [76] |■ + 0.018 [61] | + 0.020 [17] | + 0.022 [4] | + + +Latency distribution: + 10% in 0.0001 secs + 25% in 0.0001 secs + 50% in 0.0011 secs + 75% in 0.0052 secs + 90% in 0.0083 secs + 95% in 0.0102 secs + 99% in 0.0146 secs + +Details (average, fastest, slowest): + DNS+dialup: 0.0000 secs, 0.0001 secs, 0.0220 secs + DNS-lookup: 0.0000 secs, 0.0000 secs, 0.0067 secs + req write: 0.0001 secs, 0.0000 secs, 0.0106 secs + resp wait: 0.0028 secs, 0.0000 secs, 0.0220 secs + resp read: 0.0001 secs, 0.0000 secs, 0.0126 secs + +Status code distribution: + [200] 10000 responses + + + +``` diff --git a/docs/performance/index.md b/docs/performance/index.md new file mode 100644 index 000000000..00fd2b6e2 --- /dev/null +++ b/docs/performance/index.md @@ -0,0 +1,11 @@ +--- +id: performance-index +title: Introduction +--- + +Our ecosystem is not another over-hyped, oversold technology stack. Our technology is truly built with 12 factor principles +in mind, and the technology's performance metrics are as good as it gets. + +To prove this to you, we run an automated benchmark test suite **on every product on every push to master** +(well, almost - benchmarks for ORY Oathkeeper and ORY Keto will follow soon). You can +select the product you would like to know more about from the navigation on the left. diff --git a/markdown-link-check.json b/markdown-link-check.json new file mode 100644 index 000000000..05c8934f9 --- /dev/null +++ b/markdown-link-check.json @@ -0,0 +1,32 @@ +{ + "ignorePatterns": [ + {"pattern": "^(https|http)://mydomain.com"}, + {"pattern": "^(https|http)://mydomain/"}, + {"pattern": "^http://hydra"}, + {"pattern": "^(http|https)://localhost"}, + {"pattern": "^(http|https)://127.0.0.1"}, + {"pattern": "^/oauth2/auth"}, + {"pattern": "^/oauth2/token"} + ], + "replacementPatterns": [ + { + "pattern": "../images/docs", + "replacement": "../website/static/images/docs" + }, + { + "pattern": "../../images/docs", + "replacement": "../../website/static/images/docs" + }, + { + "pattern": "../../../images/docs", + "replacement": "../../../website/static/images/docs" + } + ], + "httpHeaders": [ + { + "urls": [ + "https://example.com" + ] + } + ] +} \ No newline at end of file diff --git a/scripts/test-links.sh b/scripts/test-links.sh new file mode 100755 index 000000000..32d1289fb --- /dev/null +++ b/scripts/test-links.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +set -euo pipefail + +cd "$( dirname "${BASH_SOURCE[0]}" )/.." + +find docs/ -name \*.md -exec ./website/node_modules/.bin/markdown-link-check -c markdown-link-check.json --quiet {} \; diff --git a/website/README.md b/website/README.md new file mode 100644 index 000000000..f3da77ff3 --- /dev/null +++ b/website/README.md @@ -0,0 +1,193 @@ +This website was created with [Docusaurus](https://docusaurus.io/). + +# What's In This Document + +* [Get Started in 5 Minutes](#get-started-in-5-minutes) +* [Directory Structure](#directory-structure) +* [Editing Content](#editing-content) +* [Adding Content](#adding-content) +* [Full Documentation](#full-documentation) + +# Get Started in 5 Minutes + +1. Make sure all the dependencies for the website are installed: + +```sh +# Install dependencies +$ yarn +``` +2. Run your dev server: + +```sh +# Start the site +$ yarn start +``` + +## Directory Structure + +Your project file structure should look something like this + +``` +my-docusaurus/ + docs/ + doc-1.md + doc-2.md + doc-3.md + website/ + blog/ + 2016-3-11-oldest-post.md + 2017-10-24-newest-post.md + core/ + node_modules/ + pages/ + static/ + css/ + img/ + package.json + sidebar.json + siteConfig.js +``` + +# Editing Content + +## Editing an existing docs page + +Edit docs by navigating to `docs/` and editing the corresponding document: + +`docs/doc-to-be-edited.md` + +```markdown +--- +id: page-needs-edit +title: This Doc Needs To Be Edited +--- + +Edit me... +``` + +For more information about docs, click [here](https://docusaurus.io/docs/en/navigation) + +## Editing an existing blog post + +Edit blog posts by navigating to `website/blog` and editing the corresponding post: + +`website/blog/post-to-be-edited.md` +```markdown +--- +id: post-needs-edit +title: This Blog Post Needs To Be Edited +--- + +Edit me... +``` + +For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog) + +# Adding Content + +## Adding a new docs page to an existing sidebar + +1. Create the doc as a new markdown file in `/docs`, example `docs/newly-created-doc.md`: + +```md +--- +id: newly-created-doc +title: This Doc Needs To Be Edited +--- + +My new content here.. +``` + +1. Refer to that doc's ID in an existing sidebar in `website/sidebar.json`: + +```javascript +// Add newly-created-doc to the Getting Started category of docs +{ + "docs": { + "Getting Started": [ + "quick-start", + "newly-created-doc" // new doc here + ], + ... + }, + ... +} +``` + +For more information about adding new docs, click [here](https://docusaurus.io/docs/en/navigation) + +## Adding a new blog post + +1. Make sure there is a header link to your blog in `website/siteConfig.js`: + +`website/siteConfig.js` +```javascript +headerLinks: [ + ... + { blog: true, label: 'Blog' }, + ... +] +``` + +2. Create the blog post with the format `YYYY-MM-DD-My-Blog-Post-Title.md` in `website/blog`: + +`website/blog/2018-05-21-New-Blog-Post.md` + +```markdown +--- +author: Frank Li +authorURL: https://twitter.com/foobarbaz +authorFBID: 503283835 +title: New Blog Post +--- + +Lorem Ipsum... +``` + +For more information about blog posts, click [here](https://docusaurus.io/docs/en/adding-blog) + +## Adding items to your site's top navigation bar + +1. Add links to docs, custom pages or external links by editing the headerLinks field of `website/siteConfig.js`: + +`website/siteConfig.js` +```javascript +{ + headerLinks: [ + ... + /* you can add docs */ + { doc: 'my-examples', label: 'Examples' }, + /* you can add custom pages */ + { page: 'help', label: 'Help' }, + /* you can add external links */ + { href: 'https://github.com/facebook/Docusaurus', label: 'GitHub' }, + ... + ], + ... +} +``` + +For more information about the navigation bar, click [here](https://docusaurus.io/docs/en/navigation) + +## Adding custom pages + +1. Docusaurus uses React components to build pages. The components are saved as .js files in `website/pages/en`: +1. If you want your page to show up in your navigation header, you will need to update `website/siteConfig.js` to add to the `headerLinks` element: + +`website/siteConfig.js` +```javascript +{ + headerLinks: [ + ... + { page: 'my-new-custom-page', label: 'My New Custom Page' }, + ... + ], + ... +} +``` + +For more information about custom pages, click [here](https://docusaurus.io/docs/en/custom-pages). + +# Full Documentation + +Full documentation can be found on the [website](https://docusaurus.io/). diff --git a/website/core/Footer.js b/website/core/Footer.js new file mode 100644 index 000000000..fcfcfe8e6 --- /dev/null +++ b/website/core/Footer.js @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require('react'); + +const script = `(function () { + document.querySelectorAll('.tabs .tabs-nav .nav-item a').forEach(function (t) { + t.addEventListener("click", function (e) { + e.preventDefault(); + + t.closest('.tabs-nav').querySelectorAll('.nav-item a').forEach(function (i) { + i.classList.remove('active'); + }); + + t.closest('.tabs').querySelectorAll('.tab-content .tab-pane').forEach(function (i) { + i.classList.remove('active'); + }); + + t.classList.add('active'); + document.getElementById(t.href.split('#')[1]).classList.add('active'); + return false + }); + }); +})();` + +// + +class Footer extends React.Component { + render() { + return ( +