baisc structure and server begin server impl
This commit is contained in:
parent
26bd873f4e
commit
f6ba7ab75e
17 changed files with 575 additions and 0 deletions
1
docs.go
Normal file
1
docs.go
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package oidc
|
12
example/internal/mock/storage.go
Normal file
12
example/internal/mock/storage.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package mock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/caos/oidc/pkg/oidc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Storage struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Storage) CreateAuthRequest(authReq *oidc.AuthRequest) error {
|
||||||
|
return nil
|
||||||
|
}
|
18
example/server/default/default.go
Normal file
18
example/server/default/default.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/caos/oidc/example/internal/mock"
|
||||||
|
"github.com/caos/oidc/pkg/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
config := &server.Config{
|
||||||
|
Issuer: "test",
|
||||||
|
}
|
||||||
|
storage := &mock.Storage{}
|
||||||
|
handler := server.NewDefaultHandler(config, storage)
|
||||||
|
server.Start(ctx, handler)
|
||||||
|
}
|
12
go.mod
Normal file
12
go.mod
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module github.com/caos/oidc
|
||||||
|
|
||||||
|
go 1.13
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/caos/utils/logging v0.0.0-20191104132131-b318678afbef
|
||||||
|
github.com/gorilla/mux v1.7.3
|
||||||
|
github.com/stretchr/testify v1.4.0
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||||
|
golang.org/x/text v0.3.2
|
||||||
|
gopkg.in/square/go-jose.v2 v2.4.0
|
||||||
|
)
|
92
go.sum
Normal file
92
go.sum
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/caos/utils v0.0.0-20191104132131-b318678afbef h1:/a781PnuLvuTOj0PEJ7ByhgqjpC30Jsk+11uGcuxjjA=
|
||||||
|
github.com/caos/utils/config v0.0.0-20191002113340-78986eef31d3/go.mod h1:4iI2a+qaiRFiLV1RAPG5dLp67M+NP2832toQbG9Uu74=
|
||||||
|
github.com/caos/utils/logging v0.0.0-20191104132131-b318678afbef h1:ZkyR2deIvTjvULYw6bInjRR4YNfktQ9F6/z0VijtfmU=
|
||||||
|
github.com/caos/utils/logging v0.0.0-20191104132131-b318678afbef/go.mod h1:1QHTbh4VS/6qN5fOApyEa70ESW+nDri3XE7j3oIzbyU=
|
||||||
|
github.com/caos/utils/pairs v0.0.0-20191002113340-78986eef31d3 h1:HYlIp17vhqqUc9YOURRHOX948dSNfGYMo+aC+oaJvb0=
|
||||||
|
github.com/caos/utils/pairs v0.0.0-20191002113340-78986eef31d3/go.mod h1:UZHeoVF6vhET4wTA/alcu0KOy65P2WI3AFlMvThGDcE=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||||
|
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.11.3 h1:h8+NsYENhxNTuq+dobk3+ODoJtwY4Fu0WQXsxJfL8aM=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway v1.11.3/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
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/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||||
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
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.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191002091554-b397fe3ad8ed h1:5TJcLJn2a55mJjzYk0yOoqN8X1OdvBDUnaZaKKyQtkY=
|
||||||
|
golang.org/x/sys v0.0.0-20191002091554-b397fe3ad8ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c h1:hrpEMCZ2O7DR5gC1n2AJGVhrwiEjOi35+jxtIuZpTMo=
|
||||||
|
google.golang.org/genproto v0.0.0-20190927181202-20e1ac93f88c/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.24.0 h1:vb/1TCsVn3DcJlQ0Gs1yB1pKI6Do2/QNwxdKqmc/b0s=
|
||||||
|
google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
|
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
50
pkg/oidc/authorization.go
Normal file
50
pkg/oidc/authorization.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package oidc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ResponseTypeCode = "code"
|
||||||
|
ResponseTypeIDToken = "id_token token"
|
||||||
|
ResponseTypeIDTokenOnly = "id_token"
|
||||||
|
|
||||||
|
DisplayPage = "page"
|
||||||
|
DisplayPopup = "popup"
|
||||||
|
DisplayTouch = "touch"
|
||||||
|
DisplayWAP = "wap"
|
||||||
|
|
||||||
|
PromptNone = "none"
|
||||||
|
PromptLogin = "login"
|
||||||
|
PromptConsent = "consent"
|
||||||
|
PromptSelectAccount = "select_account"
|
||||||
|
)
|
||||||
|
|
||||||
|
//AuthRequest according to:
|
||||||
|
//https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||||
|
//
|
||||||
|
type AuthRequest struct {
|
||||||
|
Scopes []string `schema:"scope"`
|
||||||
|
ResponseType ResponseType `schema:"response_type"`
|
||||||
|
ClientID string
|
||||||
|
RedirectURI string //TODO: type
|
||||||
|
|
||||||
|
State string
|
||||||
|
|
||||||
|
// ResponseMode TODO: ?
|
||||||
|
|
||||||
|
Nonce string
|
||||||
|
Display Display
|
||||||
|
Prompt Prompt
|
||||||
|
MaxAge uint32
|
||||||
|
UILocales []language.Tag
|
||||||
|
IDTokenHint string
|
||||||
|
LoginHint string
|
||||||
|
ACRValues []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResponseType string
|
||||||
|
|
||||||
|
type Display string
|
||||||
|
|
||||||
|
type Prompt string
|
33
pkg/oidc/client_credentials.go
Normal file
33
pkg/oidc/client_credentials.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package oidc
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type clientCredentialsGrantBasic struct {
|
||||||
|
grantType string `schema:"grant_type"`
|
||||||
|
scope string `schema:"scope"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type clientCredentialsGrant struct {
|
||||||
|
*clientCredentialsGrantBasic
|
||||||
|
clientID string `schema:"client_id"`
|
||||||
|
clientSecret string `schema:"client_secret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//ClientCredentialsGrantBasic creates an oauth2 `Client Credentials` Grant
|
||||||
|
//sneding client_id and client_secret as basic auth header
|
||||||
|
func ClientCredentialsGrantBasic(scopes ...string) *clientCredentialsGrantBasic {
|
||||||
|
return &clientCredentialsGrantBasic{
|
||||||
|
grantType: "client_credentials",
|
||||||
|
scope: strings.Join(scopes, " "),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ClientCredentialsGrantValues creates an oauth2 `Client Credentials` Grant
|
||||||
|
//sneding client_id and client_secret as form values
|
||||||
|
func ClientCredentialsGrantValues(clientID, clientSecret string, scopes ...string) *clientCredentialsGrant {
|
||||||
|
return &clientCredentialsGrant{
|
||||||
|
clientCredentialsGrantBasic: ClientCredentialsGrantBasic(scopes...),
|
||||||
|
clientID: clientID,
|
||||||
|
clientSecret: clientSecret,
|
||||||
|
}
|
||||||
|
}
|
24
pkg/oidc/discovery.go
Normal file
24
pkg/oidc/discovery.go
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package oidc
|
||||||
|
|
||||||
|
const (
|
||||||
|
DiscoveryEndpoint = "/.well-known/openid-configuration"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DiscoveryConfiguration struct {
|
||||||
|
Issuer string `json:"issuer,omitempty"`
|
||||||
|
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"`
|
||||||
|
TokenEndpoint string `json:"token_endpoint,omitempty"`
|
||||||
|
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
|
||||||
|
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
|
||||||
|
EndSessionEndpoint string `json:"end_session_endpoint,omitempty"`
|
||||||
|
CheckSessionIframe string `json:"check_session_iframe,omitempty"`
|
||||||
|
JwksURI string `json:"jwks_uri,omitempty"`
|
||||||
|
ScopesSupported []string `json:"scopes_supported,omitempty"`
|
||||||
|
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
|
||||||
|
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
|
||||||
|
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
|
||||||
|
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
|
||||||
|
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
|
||||||
|
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`
|
||||||
|
ClaimsSupported []string `json:"claims_supported,omitempty"`
|
||||||
|
}
|
84
pkg/oidc/token.go
Normal file
84
pkg/oidc/token.go
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package oidc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"gopkg.in/square/go-jose.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IDTokenClaims struct {
|
||||||
|
Issuer string `json:"iss,omitempty"`
|
||||||
|
Subject string `json:"sub,omitempty"`
|
||||||
|
Audiences []string `json:"aud,omitempty"`
|
||||||
|
Expiration time.Time `json:"exp,omitempty"`
|
||||||
|
IssuedAt time.Time `json:"iat,omitempty"`
|
||||||
|
AuthTime time.Time `json:"auth_time,omitempty"`
|
||||||
|
Nonce string `json:"nonce,omitempty"`
|
||||||
|
AuthenticationContextClassReference string `json:"acr,omitempty"`
|
||||||
|
AuthenticationMethodsReferences []string `json:"amr,omitempty"`
|
||||||
|
AuthorizedParty string `json:"azp,omitempty"`
|
||||||
|
AccessTokenHash string `json:"at_hash,omitempty"`
|
||||||
|
|
||||||
|
Signature jose.SignatureAlgorithm //TODO: ???
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *IDTokenClaims) UnmarshalJSON(b []byte) error {
|
||||||
|
var i jsonIDToken
|
||||||
|
if err := json.Unmarshal(b, &i); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.Issuer = i.Issuer
|
||||||
|
t.Subject = i.Subject
|
||||||
|
// t.Audiences = strings.Split(i.Audiences, " ")
|
||||||
|
t.Audiences = i.Audiences
|
||||||
|
t.Expiration = time.Unix(i.Expiration, 0).UTC()
|
||||||
|
t.IssuedAt = time.Unix(i.IssuedAt, 0).UTC()
|
||||||
|
t.AuthTime = time.Unix(i.AuthTime, 0).UTC()
|
||||||
|
t.Nonce = i.Nonce
|
||||||
|
t.AuthenticationContextClassReference = i.AuthenticationContextClassReference
|
||||||
|
t.AuthenticationMethodsReferences = i.AuthenticationMethodsReferences
|
||||||
|
t.AuthorizedParty = i.AuthorizedParty
|
||||||
|
t.AccessTokenHash = i.AccessTokenHash
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *IDTokenClaims) MarshalJSON() ([]byte, error) {
|
||||||
|
j := jsonIDToken{
|
||||||
|
Issuer: t.Issuer,
|
||||||
|
Subject: t.Subject,
|
||||||
|
// Audiences: strings.Join(t.Audiences, " "),
|
||||||
|
Audiences: t.Audiences,
|
||||||
|
Expiration: t.Expiration.Unix(),
|
||||||
|
IssuedAt: t.IssuedAt.Unix(),
|
||||||
|
AuthTime: t.AuthTime.Unix(),
|
||||||
|
Nonce: t.Nonce,
|
||||||
|
AuthenticationContextClassReference: t.AuthenticationContextClassReference,
|
||||||
|
AuthenticationMethodsReferences: t.AuthenticationMethodsReferences,
|
||||||
|
AuthorizedParty: t.AuthorizedParty,
|
||||||
|
AccessTokenHash: t.AccessTokenHash,
|
||||||
|
}
|
||||||
|
return json.Marshal(j)
|
||||||
|
}
|
||||||
|
|
||||||
|
// type jsonTime time.Time
|
||||||
|
|
||||||
|
type jsonIDToken struct {
|
||||||
|
Issuer string `json:"iss,omitempty"`
|
||||||
|
Subject string `json:"sub,omitempty"`
|
||||||
|
Audiences []string `json:"aud,omitempty"`
|
||||||
|
Expiration int64 `json:"exp,omitempty"`
|
||||||
|
IssuedAt int64 `json:"iat,omitempty"`
|
||||||
|
AuthTime int64 `json:"auth_time,omitempty"`
|
||||||
|
Nonce string `json:"nonce,omitempty"`
|
||||||
|
AuthenticationContextClassReference string `json:"acr,omitempty"`
|
||||||
|
AuthenticationMethodsReferences []string `json:"amr,omitempty"`
|
||||||
|
AuthorizedParty string `json:"azp,omitempty"`
|
||||||
|
AccessTokenHash string `json:"at_hash,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tokens struct {
|
||||||
|
*oauth2.Token
|
||||||
|
IDTokenClaims *IDTokenClaims
|
||||||
|
}
|
16
pkg/server/authrequest.go
Normal file
16
pkg/server/authrequest.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/caos/oidc/pkg/oidc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseAuthRequest(w http.ResponseWriter, r *http.Request) (*oidc.AuthRequest, error) {
|
||||||
|
return nil, errors.New("Unimplemented") //TODO: impl
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateAuthRequest(authRequest *oidc.AuthRequest) error {
|
||||||
|
return errors.New("Unimplemented") //TODO: impl https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.2
|
||||||
|
}
|
9
pkg/server/config.go
Normal file
9
pkg/server/config.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
type Configuration interface {
|
||||||
|
Issuer() string
|
||||||
|
AuthorizationEndpoint() string
|
||||||
|
TokenEndpoint() string
|
||||||
|
UserinfoEndpoint() string
|
||||||
|
Port() string
|
||||||
|
}
|
101
pkg/server/default_handler.go
Normal file
101
pkg/server/default_handler.go
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/caos/oidc/pkg/utils"
|
||||||
|
|
||||||
|
"github.com/caos/oidc/pkg/oidc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DefaultHandler struct {
|
||||||
|
config *Config
|
||||||
|
discoveryConfig *oidc.DiscoveryConfiguration
|
||||||
|
storage Storage
|
||||||
|
http *http.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Issuer string
|
||||||
|
AuthorizationEndpoint string
|
||||||
|
TokenEndpoint string
|
||||||
|
UserinfoEndpoint string
|
||||||
|
Port string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) OIDC() *oidc.DiscoveryConfiguration {
|
||||||
|
return &oidc.DiscoveryConfiguration{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultHandler(config *Config, storage Storage) Handler {
|
||||||
|
h := &DefaultHandler{
|
||||||
|
config: config,
|
||||||
|
discoveryConfig: config.OIDC(),
|
||||||
|
storage: storage,
|
||||||
|
}
|
||||||
|
router := CreateRouter(h)
|
||||||
|
h.http = &http.Server{
|
||||||
|
Addr: config.Port,
|
||||||
|
Handler: router,
|
||||||
|
}
|
||||||
|
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) Issuer() string {
|
||||||
|
return h.config.Issuer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) AuthorizationEndpoint() string {
|
||||||
|
return h.config.AuthorizationEndpoint
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) TokenEndpoint() string {
|
||||||
|
return h.config.TokenEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) UserinfoEndpoint() string {
|
||||||
|
return h.config.UserinfoEndpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) Port() string {
|
||||||
|
return h.config.Port
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) HttpHandler() *http.Server {
|
||||||
|
return h.http
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) HandleDiscovery(w http.ResponseWriter, r *http.Request) {
|
||||||
|
utils.MarshalJSON(w, h.discoveryConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) HandleAuthorize(w http.ResponseWriter, r *http.Request) {
|
||||||
|
authRequest, err := ParseAuthRequest(w, r)
|
||||||
|
if err != nil {
|
||||||
|
//TODO: return err
|
||||||
|
}
|
||||||
|
err = ValidateAuthRequest(authRequest)
|
||||||
|
if err != nil {
|
||||||
|
//TODO: return err
|
||||||
|
}
|
||||||
|
if NeedsExistingSession(authRequest) {
|
||||||
|
// session, err := h.storage.CheckSession(authRequest)
|
||||||
|
// if err != nil {
|
||||||
|
// //TODO: return err
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
err = h.storage.CreateAuthRequest(authRequest)
|
||||||
|
if err != nil {
|
||||||
|
//TODO: return err
|
||||||
|
}
|
||||||
|
//TODO: redirect?
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) HandleExchange(w http.ResponseWriter, r *http.Request) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *DefaultHandler) HandleUserinfo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
}
|
47
pkg/server/default_handler_test.go
Normal file
47
pkg/server/default_handler_test.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/caos/oidc/pkg/oidc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDefaultHandler_HandleDiscovery(t *testing.T) {
|
||||||
|
type fields struct {
|
||||||
|
config *Config
|
||||||
|
discoveryConfig *oidc.DiscoveryConfiguration
|
||||||
|
storage Storage
|
||||||
|
http *http.Server
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
w http.ResponseWriter
|
||||||
|
r *http.Request
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields fields
|
||||||
|
args args
|
||||||
|
want string
|
||||||
|
wantCode int
|
||||||
|
}{
|
||||||
|
{"OK", fields{config: nil, discoveryConfig: &oidc.DiscoveryConfiguration{Issuer: "test"}}, args{httptest.NewRecorder(), nil}, `{"issuer":"test"}`, 200},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
h := &DefaultHandler{
|
||||||
|
config: tt.fields.config,
|
||||||
|
discoveryConfig: tt.fields.discoveryConfig,
|
||||||
|
storage: tt.fields.storage,
|
||||||
|
http: tt.fields.http,
|
||||||
|
}
|
||||||
|
h.HandleDiscovery(tt.args.w, tt.args.r)
|
||||||
|
rec := tt.args.w.(*httptest.ResponseRecorder)
|
||||||
|
require.Equal(t, tt.want, rec.Body.String())
|
||||||
|
require.Equal(t, tt.wantCode, rec.Code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
44
pkg/server/handler.go
Normal file
44
pkg/server/handler.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
"github.com/caos/oidc/pkg/oidc"
|
||||||
|
"github.com/caos/utils/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
Configuration
|
||||||
|
// Storage() Storage
|
||||||
|
HandleDiscovery(w http.ResponseWriter, r *http.Request)
|
||||||
|
HandleAuthorize(w http.ResponseWriter, r *http.Request)
|
||||||
|
HandleExchange(w http.ResponseWriter, r *http.Request)
|
||||||
|
HandleUserinfo(w http.ResponseWriter, r *http.Request)
|
||||||
|
HttpHandler() *http.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateRouter(h Handler) *mux.Router {
|
||||||
|
router := mux.NewRouter()
|
||||||
|
router.HandleFunc(oidc.DiscoveryEndpoint, h.HandleDiscovery)
|
||||||
|
router.HandleFunc(h.AuthorizationEndpoint(), h.HandleAuthorize)
|
||||||
|
router.HandleFunc(h.TokenEndpoint(), h.HandleExchange)
|
||||||
|
router.HandleFunc(h.UserinfoEndpoint(), h.HandleUserinfo)
|
||||||
|
return router
|
||||||
|
}
|
||||||
|
|
||||||
|
func Start(ctx context.Context, h Handler) {
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
err := h.HttpHandler().Shutdown(ctx)
|
||||||
|
logging.Log("SERVE-REqwpM").OnError(err).Error("graceful shutdown of oidc server failed")
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
err := h.HttpHandler().ListenAndServe()
|
||||||
|
logging.Log("SERVE-4YNIwG").OnError(err).Panic("oidc server serve failed")
|
||||||
|
}()
|
||||||
|
logging.LogWithFields("SERVE-koAFMs", "port", h.Port()).Info("oidc server is listening")
|
||||||
|
}
|
7
pkg/server/session.go
Normal file
7
pkg/server/session.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import "github.com/caos/oidc/pkg/oidc"
|
||||||
|
|
||||||
|
func NeedsExistingSession(authRequest *oidc.AuthRequest) bool {
|
||||||
|
return authRequest.IDTokenHint != "" //TODO: impl: https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.2
|
||||||
|
}
|
7
pkg/server/storage.go
Normal file
7
pkg/server/storage.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package server
|
||||||
|
|
||||||
|
import "github.com/caos/oidc/pkg/oidc"
|
||||||
|
|
||||||
|
type Storage interface {
|
||||||
|
CreateAuthRequest(*oidc.AuthRequest) error
|
||||||
|
}
|
18
pkg/utils/marshal.go
Normal file
18
pkg/utils/marshal.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/caos/utils/logging"
|
||||||
|
)
|
||||||
|
|
||||||
|
func MarshalJSON(w http.ResponseWriter, i interface{}) {
|
||||||
|
b, err := json.Marshal(i)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = w.Write(b)
|
||||||
|
logging.Log("UTILS-zVu9OW").OnError(err).Error("error writing response")
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue