Merge branch 'main' into op

This commit is contained in:
Livio Amstutz 2021-09-27 08:17:20 +02:00
commit f90e685c76
17 changed files with 706 additions and 52 deletions

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,38 @@
---
name: 🐛 Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1 @@
blank_issues_enabled: true

View file

@ -0,0 +1,20 @@
---
name: 🚀 Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View file

@ -1,11 +1,18 @@
name: Release
on: push
on:
push:
branches:
- '**'
tags-ignore:
- '**'
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-18.04
strategy:
matrix:
go: ['1.14', '1.15', '1.16']
go: ['1.14', '1.15', '1.16', '1.17']
name: Go ${{ matrix.go }} test
steps:
- uses: actions/checkout@v2
@ -14,13 +21,14 @@ jobs:
with:
go-version: ${{ matrix.go }}
- run: go test -race -v -coverprofile=profile.cov ./pkg/...
- uses: codecov/codecov-action@v1
- uses: codecov/codecov-action@v2.1.0
with:
file: ./profile.cov
name: codecov-go
release:
runs-on: ubuntu-18.04
needs: [test]
if: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:

View file

@ -1,8 +1,8 @@
module.exports = {
branch: 'master',
branches: ["main"],
plugins: [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/github"
]
};
};

128
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
abuse@zitadel.ch.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

40
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,40 @@
# How to contribute to the OIDC SDK for Go
## Did you find a bug?
Please file an issue [here](https://github.com/caos/oidc/issues/new?assignees=&labels=bug&template=bug_report.md&title=).
Bugs are evaluated every day as soon as possible.
## Enhancement
Do you miss a feature? Please file an issue [here](https://github.com/caos/oidc/issues/new?assignees=&labels=enhancement&template=feature_request.md&title=)
Enhancements are discussed and evaluated every Wednesday by the ZITADEL core team.
## Grab an Issues
We add the label "good first issue" for problems we think are a good starting point to contribute to the OIDC SDK.
* [Issues for first time contributors](https://github.com/caos/oidc/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22)
* [All issues](https://github.com/caos/oidc/issues)
### Make a PR
If you like to contribute fork the OIDC repository. After you implemented the new feature create a PullRequest in the OIDC reposiotry.
Make sure you use semantic release:
* feat: New Feature
* fix: Bug Fix
* docs: Documentation
## Want to use the library?
Checkout the [examples folder](example) for different client and server implementations.
Or checkout how we use it ourselves in our OpenSource Identity and Access Management [ZITADEL](https://github.com/caos/zitadel).
## **Did you find a security flaw?**
* Please read [Security Policy](SECURITY.md).

View file

@ -7,11 +7,13 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/caos/oidc)](https://goreportcard.com/report/github.com/caos/oidc)
[![codecov](https://codecov.io/gh/caos/oidc/branch/master/graph/badge.svg)](https://codecov.io/gh/caos/oidc)
> This project is in beta state. It can AND will continue breaking until version 1.0.0 is released
![openid_certified](https://cloud.githubusercontent.com/assets/1454075/7611268/4d19de32-f97b-11e4-895b-31b2455a7ca6.png)
## What Is It
This project is a easy to use client and server implementation for the `OIDC` (Open ID Connect) standard written for `Go`.
This project is a easy to use client (RP) and server (OP) implementation for the `OIDC` (Open ID Connect) standard written for `Go`.
The RP is certified for the [basic](https://www.certification.openid.net/plan-detail.html?public=true&plan=uoprP0OO8Z4Qo) and [config](https://www.certification.openid.net/plan-detail.html?public=true&plan=AYSdLbzmWbu9X) profile.
Whenever possible we tried to reuse / extend existing packages like `OAuth2 for Go`.
@ -44,6 +46,7 @@ For your convenience you can find the relevant standards linked below.
| 1.14 | :white_check_mark: |
| 1.15 | :white_check_mark: |
| 1.16 | :white_check_mark: |
| 1.17 | :white_check_mark: |
## Why another library
@ -51,7 +54,6 @@ As of 2020 there are not a lot of `OIDC` library's in `Go` which can handle serv
### Goals
- [Certify this library as RP](https://openid.net/certification/#RPs)
- [Certify this library as OP](https://openid.net/certification/#OPs)
### Other Go OpenID Connect library's

4
go.mod
View file

@ -7,7 +7,7 @@ require (
github.com/golang/mock v1.6.0
github.com/google/go-cmp v0.5.2 // indirect
github.com/google/go-github/v31 v31.0.0
github.com/google/uuid v1.2.0
github.com/google/uuid v1.3.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/gorilla/schema v1.2.0
@ -16,7 +16,7 @@ require (
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
golang.org/x/text v0.3.6
golang.org/x/text v0.3.7
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/square/go-jose.v2 v2.6.0
)

26
go.sum
View file

@ -50,9 +50,13 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -95,6 +99,7 @@ github.com/google/go-github/v31 v31.0.0 h1:JJUxlP9lFK+ziXKimTCprajMApV1ecWD4NB6C
github.com/google/go-github/v31 v31.0.0/go.mod h1:NQPZol8/1sMoWYGN2yaALIBytu17gAWfhbweiEed3pM=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@ -105,8 +110,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
@ -120,6 +125,7 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@ -129,21 +135,30 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rs/cors v1.8.0 h1:P2KMzcFwrPoSjkF1WLRPsp3UMLyql8L4v9hQpVeK5so=
github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -244,6 +259,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -272,8 +288,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -403,6 +419,8 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View file

@ -3,26 +3,41 @@ package rp
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"sync"
"github.com/caos/oidc/pkg/utils"
"gopkg.in/square/go-jose.v2"
"github.com/caos/oidc/pkg/oidc"
)
func NewRemoteKeySet(client *http.Client, jwksURL string) oidc.KeySet {
return &remoteKeySet{httpClient: client, jwksURL: jwksURL}
func NewRemoteKeySet(client *http.Client, jwksURL string, opts ...func(*remoteKeySet)) oidc.KeySet {
keyset := &remoteKeySet{httpClient: client, jwksURL: jwksURL}
for _, opt := range opts {
opt(keyset)
}
return keyset
}
//SkipRemoteCheck will suppress checking for new remote keys if signature validation fails with cached keys
//and no kid header is set in the JWT
//
//this might be handy to save some unnecessary round trips in cases where the JWT does not contain a kid header and
//there is only a single remote key
//please notice that remote keys will then only be fetched if cached keys are empty
func SkipRemoteCheck() func(set *remoteKeySet) {
return func(set *remoteKeySet) {
set.skipRemoteCheck = true
}
}
type remoteKeySet struct {
jwksURL string
httpClient *http.Client
defaultAlg string
jwksURL string
httpClient *http.Client
defaultAlg string
skipRemoteCheck bool
// guard all other fields
mu sync.Mutex
@ -72,23 +87,67 @@ func (r *remoteKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSig
if alg == "" {
alg = r.defaultAlg
}
keys := r.keysFromCache()
key, ok := oidc.FindKey(keyID, oidc.KeyUseSignature, alg, keys...)
if ok && keyID != "" {
payload, err := jws.Verify(&key)
return payload, err
payload, err := r.verifySignatureCached(jws, keyID, alg)
if payload != nil {
return payload, nil
}
if err != nil {
return nil, err
}
return r.verifySignatureRemote(ctx, jws, keyID, alg)
}
//verifySignatureCached checks for a matching key in the cached key list
//
//if there is only one possible, it tries to verify the signature and will return the payload if successful
//
//it only returns an error if signature validation fails and keys exactMatch which is if either:
// - both kid are empty and skipRemoteCheck is set to true
// - or both (JWT and JWK) kid are equal
//
//otherwise it will return no error (so remote keys will be loaded)
func (r *remoteKeySet) verifySignatureCached(jws *jose.JSONWebSignature, keyID, alg string) ([]byte, error) {
keys := r.keysFromCache()
if len(keys) == 0 {
return nil, nil
}
key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, keys...)
if err != nil {
//no key / multiple found, try with remote keys
return nil, nil //nolint:nilerr
}
payload, err := jws.Verify(&key)
if payload != nil {
return payload, nil
}
if !r.exactMatch(key.KeyID, keyID) {
//no exact key match, try getting better match with remote keys
return nil, nil
}
return nil, fmt.Errorf("signature verification failed: %w", err)
}
func (r *remoteKeySet) exactMatch(jwkID, jwsID string) bool {
if jwkID == "" && jwsID == "" {
return r.skipRemoteCheck
}
return jwkID == jwsID
}
func (r *remoteKeySet) verifySignatureRemote(ctx context.Context, jws *jose.JSONWebSignature, keyID, alg string) ([]byte, error) {
keys, err := r.keysFromRemote(ctx)
if err != nil {
return nil, fmt.Errorf("fetching keys %v", err)
return nil, fmt.Errorf("unable to fetch key for signature validation: %w", err)
}
key, ok = oidc.FindKey(keyID, oidc.KeyUseSignature, alg, keys...)
if ok {
payload, err := jws.Verify(&key)
return payload, err
key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, keys...)
if err != nil {
return nil, fmt.Errorf("unable to validate signature: %w", err)
}
return nil, errors.New("invalid key")
payload, err := jws.Verify(&key)
if err != nil {
return nil, fmt.Errorf("signature verification failed: %w", err)
}
return payload, nil
}
func (r *remoteKeySet) keysFromCache() (keys []jose.JSONWebKey) {

View file

@ -2,6 +2,7 @@ package rp
import (
"context"
"encoding/base64"
"errors"
"net/http"
"strings"
@ -288,7 +289,7 @@ func AuthURLHandler(stateFn func() string, rp RelyingParty) http.HandlerFunc {
//GenerateAndStoreCodeChallenge generates a PKCE code challenge and stores its verifier into a secure cookie
func GenerateAndStoreCodeChallenge(w http.ResponseWriter, rp RelyingParty) (string, error) {
codeVerifier := uuid.New().String()
codeVerifier := base64.RawURLEncoding.EncodeToString([]byte(uuid.New().String()))
if err := rp.CookieHandler().SetCookie(w, pkceCode, codeVerifier); err != nil {
return "", err
}

View file

@ -5,6 +5,7 @@ import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"errors"
"gopkg.in/square/go-jose.v2"
)
@ -13,6 +14,11 @@ const (
KeyUseSignature = "sig"
)
var (
ErrKeyMultiple = errors.New("multiple possible keys match")
ErrKeyNone = errors.New("no possible keys matches")
)
//KeySet represents a set of JSON Web Keys
// - remotely fetch via discovery and jwks_uri -> `remoteKeySet`
// - held by the OP itself in storage -> `openIDKeySet`
@ -39,20 +45,38 @@ func GetKeyIDAndAlg(jws *jose.JSONWebSignature) (string, string) {
//will return the key immediately if matches exact (id, usage, type)
//
//will return false none or multiple match
//
//deprecated: use FindMatchingKey which will return an error (more specific) instead of just a bool
//moved implementation already to FindMatchingKey
func FindKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (jose.JSONWebKey, bool) {
key, err := FindMatchingKey(keyID, use, expectedAlg, keys...)
return key, err == nil
}
//FindMatchingKey searches the given JSON Web Keys for the requested key ID, usage and key type
//
//will return the key immediately if matches exact (id, usage, type)
//
//will return a specific error if none (ErrKeyNone) or multiple (ErrKeyMultiple) match
func FindMatchingKey(keyID, use, expectedAlg string, keys ...jose.JSONWebKey) (key jose.JSONWebKey, err error) {
var validKeys []jose.JSONWebKey
for _, key := range keys {
if key.KeyID == keyID && key.Use == use && algToKeyType(key.Key, expectedAlg) {
if keyID != "" {
return key, true
for _, k := range keys {
if k.Use == use && algToKeyType(k.Key, expectedAlg) {
if k.KeyID == keyID && keyID != "" {
return k, nil
}
if k.KeyID == "" || keyID == "" {
validKeys = append(validKeys, k)
}
validKeys = append(validKeys, key)
}
}
if len(validKeys) == 1 {
return validKeys[0], true
return validKeys[0], nil
}
return jose.JSONWebKey{}, false
if len(validKeys) > 1 {
return key, ErrKeyMultiple
}
return key, ErrKeyNone
}
func algToKeyType(key interface{}, alg string) bool {

319
pkg/oidc/keyset_test.go Normal file
View file

@ -0,0 +1,319 @@
package oidc
import (
"crypto/rsa"
"errors"
"reflect"
"testing"
"gopkg.in/square/go-jose.v2"
)
func TestFindKey(t *testing.T) {
type args struct {
keyID string
use string
expectedAlg string
keys []jose.JSONWebKey
}
type res struct {
key jose.JSONWebKey
err error
}
tests := []struct {
name string
args args
res res
}{
{
"no keys, ErrKeyNone",
args{
keyID: "",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: nil,
},
res{
key: jose.JSONWebKey{},
err: ErrKeyNone,
},
},
{
"single key enc, ErrKeyNone",
args{
keyID: "",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "enc",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{},
err: ErrKeyNone,
},
},
{
"single key wrong algorithm, ErrKeyNone",
args{
keyID: "",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
Key: &rsa.PrivateKey{},
},
},
},
res{
key: jose.JSONWebKey{},
err: ErrKeyNone,
},
},
{
"single key no kid, no jwt kid, match",
args{
keyID: "",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{
Use: "sig",
Key: &rsa.PublicKey{},
},
err: nil,
},
},
{
"single key kid, jwt no kid, match",
args{
keyID: "",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
KeyID: "id",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{
Use: "sig",
KeyID: "id",
Key: &rsa.PublicKey{},
},
err: nil,
},
},
{
"single key no kid, jwt with kid, match",
args{
keyID: "id",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{
Use: "sig",
Key: &rsa.PublicKey{},
},
err: nil,
},
},
{
"single key wrong kid, ErrKeyNone",
args{
keyID: "id",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
KeyID: "id2",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{},
err: ErrKeyNone,
},
},
{
"multiple keys no kid, jwt no kid, ErrKeyMultiple",
args{
keyID: "",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
Key: &rsa.PublicKey{},
},
{
Use: "sig",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{},
err: ErrKeyMultiple,
},
},
{
"multiple keys with kid, jwt no kid, ErrKeyMultiple",
args{
keyID: "",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
KeyID: "id1",
Key: &rsa.PublicKey{},
},
{
Use: "sig",
KeyID: "id2",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{},
err: ErrKeyMultiple,
},
},
{
"multiple keys, single sig key, jwt no kid, match",
args{
keyID: "",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
Key: &rsa.PublicKey{},
},
{
Use: "enc",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{
Use: "sig",
Key: &rsa.PublicKey{},
},
err: nil,
},
},
{
"multiple keys no kid, jwt with kid, ErrKeyMultiple",
args{
keyID: "id",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
Key: &rsa.PublicKey{},
},
{
Use: "sig",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{},
err: ErrKeyMultiple,
},
},
{
"multiple keys with kid, jwt with kid, match",
args{
keyID: "id1",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
KeyID: "id1",
Key: &rsa.PublicKey{},
},
{
Use: "sig",
KeyID: "id2",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{
Use: "sig",
KeyID: "id1",
Key: &rsa.PublicKey{},
},
err: nil,
},
},
{
"multiple keys, single sig key, jwt with kid, match",
args{
keyID: "id1",
use: KeyUseSignature,
expectedAlg: "RS256",
keys: []jose.JSONWebKey{
{
Use: "sig",
Key: &rsa.PublicKey{},
},
{
Use: "enc",
Key: &rsa.PublicKey{},
},
},
},
res{
key: jose.JSONWebKey{
Use: "sig",
Key: &rsa.PublicKey{},
},
err: nil,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := FindMatchingKey(tt.args.keyID, tt.args.use, tt.args.expectedAlg, tt.args.keys...)
if (tt.res.err != nil && !errors.Is(err, tt.res.err)) || (tt.res.err == nil && err != nil) {
t.Errorf("FindKey() error, got = %v, want = %v", err, tt.res.err)
}
if !reflect.DeepEqual(got, tt.res.key) {
t.Errorf("FindKey() got = %v, want %v", got, tt.res.key)
}
})
}
}

View file

@ -39,6 +39,7 @@ var (
ErrSignatureMultiple = errors.New("id_token contains multiple signatures")
ErrSignatureUnsupportedAlg = errors.New("signature algorithm not supported")
ErrSignatureInvalidPayload = errors.New("signature does not match Payload")
ErrSignatureInvalid = errors.New("invalid signature")
ErrExpired = errors.New("token has expired")
ErrIatMissing = errors.New("issuedAt of token is missing")
ErrIatInFuture = errors.New("issuedAt of token is in the future")
@ -143,7 +144,7 @@ func CheckSignature(ctx context.Context, token string, payload []byte, claims Cl
signedPayload, err := set.VerifySignature(ctx, jws)
if err != nil {
return err
return fmt.Errorf("%w (%v)", ErrSignatureInvalid, err)
}
if !bytes.Equal(signedPayload, payload) {

View file

@ -4,7 +4,6 @@ import (
"errors"
"net/url"
"os"
"strings"
"golang.org/x/text/language"
)
@ -57,9 +56,5 @@ func devLocalAllowed(url *url.URL) bool {
if !b {
return b
}
return url.Scheme == "http" &&
url.Host == "localhost" ||
url.Host == "127.0.0.1" ||
url.Host == "::1" ||
strings.HasPrefix(url.Host, "localhost:")
return url.Scheme == "http"
}

View file

@ -2,7 +2,7 @@ package op
import (
"context"
"errors"
"fmt"
"net/http"
"time"
@ -279,12 +279,12 @@ type openIDKeySet struct {
func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) {
keySet, err := o.Storage.GetKeySet(ctx)
if err != nil {
return nil, errors.New("error fetching keys")
return nil, fmt.Errorf("error fetching keys: %w", err)
}
keyID, alg := oidc.GetKeyIDAndAlg(jws)
key, ok := oidc.FindKey(keyID, oidc.KeyUseSignature, alg, keySet.Keys...)
if !ok {
return nil, errors.New("invalid kid")
key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, keySet.Keys...)
if err != nil {
return nil, fmt.Errorf("invalid signature: %w", err)
}
return jws.Verify(&key)
}