Merge branch 'main' into op
This commit is contained in:
commit
f90e685c76
17 changed files with 706 additions and 52 deletions
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal 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
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: true
|
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal 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.
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
|
@ -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:
|
||||
|
|
|
@ -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
128
CODE_OF_CONDUCT.md
Normal 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
40
CONTRIBUTING.md
Normal 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).
|
|
@ -7,11 +7,13 @@
|
|||
[](https://goreportcard.com/report/github.com/caos/oidc)
|
||||
[](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
|
||||

|
||||
|
||||
## 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
4
go.mod
|
@ -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
26
go.sum
|
@ -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=
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
319
pkg/oidc/keyset_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
10
pkg/op/op.go
10
pkg/op/op.go
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue