mirror of
https://github.com/davidallendj/opaal.git
synced 2025-12-20 03:27:02 -07:00
Added CLI and more functionality
This commit is contained in:
parent
c04107cf3d
commit
053095c412
12 changed files with 199 additions and 92 deletions
18
cmd/config.go
Normal file
18
cmd/config.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package cmd
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Create a new default config file",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// create a new config at all args (paths)
|
||||
for _, path := range args {
|
||||
_ = path
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(configCmd)
|
||||
}
|
||||
62
cmd/login
Normal file
62
cmd/login
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package cmd
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
var (
|
||||
host string
|
||||
port int
|
||||
redirectUri = []string{""}
|
||||
state = ""
|
||||
responseType = "code"
|
||||
scope = []string{"email", "profile", "openid"}
|
||||
client oauth.Client
|
||||
)
|
||||
|
||||
var loginCmd = &cobra.Command{
|
||||
Use: "login",
|
||||
Short: "Start the login flow"
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
oidcProvider := oidc.NewOIDCProvider()
|
||||
var authorizationUrl = util.BuildAuthorizationUrl(
|
||||
oidcProvider.GetAuthorizeUrl(),
|
||||
client.Id,
|
||||
redirectUri,
|
||||
util.RandomString(20),
|
||||
responseType,
|
||||
[]string{"email", "profile", "openid"},
|
||||
)
|
||||
|
||||
// print the authorization URL for the user to log in
|
||||
fmt.Printf("Login with identity provider: %s\n", authorizationUrl)
|
||||
|
||||
// start a HTTP server to listen for callback responses
|
||||
fmt.Printf("Waiting for response from OIDC provider...\n")
|
||||
err := server.Start(host, port)
|
||||
if errors.Is(err, http.ErrServerClosed) {
|
||||
fmt.Printf("server closed\n")
|
||||
} else if err != nil {
|
||||
fmt.Printf("error starting server: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// extract code from response and exchange for bearer token
|
||||
|
||||
// extract ID token and save user info
|
||||
|
||||
// create a new identity with Ory Kratos
|
||||
|
||||
// use ID token/user info to get access token from Ory Hydra
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
func init(){
|
||||
loginCmd.Flags().StringVar(&client.Id, "client.id", "", "set the client ID")
|
||||
loginCmd.Flags().StringVar(&redirectUri, "redirect-uri", "", "set the redirect URI")
|
||||
loginCmd.Flags().StringVar(&responseType, "response-type", "code", "set the response-type")
|
||||
loginCmd.Flags().StringSliceVar(&scope, "scope", []string{"openid", "email"}, "set the scopes")
|
||||
loginCmd.Flags().String(&state, "state", util.RandomString(), "set the state")
|
||||
loginCmd.Flags().StringVar(host, "host", "127.0.0.1", "set the listening host")
|
||||
loginCmd.Flags().IntVar(&port, "port", 3333, "set the listening port")
|
||||
rootCmd.AddCommand(loginCmd)
|
||||
}
|
||||
28
cmd/root.go
Normal file
28
cmd/root.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var configPath = ""
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "oidc",
|
||||
Short: "An experimental OIDC helper tool for handling logins",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Whoops. There was an error while executing your CLI '%s'", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.Flags().StringVar(&configPath, "config", "", "set the config path")
|
||||
}
|
||||
13
go.mod
13
go.mod
|
|
@ -1,13 +1,10 @@
|
|||
module davidallendj/ochami-auth
|
||||
module davidallendj/oidc-auth
|
||||
|
||||
go 1.21.5
|
||||
go 1.22.0
|
||||
|
||||
require github.com/ory/client-go v1.6.1
|
||||
require github.com/spf13/cobra v1.8.0
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
golang.org/x/net v0.20.0 // indirect
|
||||
golang.org/x/oauth2 v0.16.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
)
|
||||
|
|
|
|||
36
go.sum
36
go.sum
|
|
@ -1,26 +1,10 @@
|
|||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/ory/client-go v1.6.1 h1:nVb1ZRtBQS9oLJQR7VK7t9cxNHXKdZ2CtpoDmmHOhAQ=
|
||||
github.com/ory/client-go v1.6.1/go.mod h1:6dx0Ir6q8O9mUvl3sqrlyR+0LalXLwwKedVDDmSPNQs=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
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/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
28
handler.go
28
handler.go
|
|
@ -1,28 +0,0 @@
|
|||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"html/template"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (app *App) dashboardHandler() http.HandlerFunc {
|
||||
return func(writer http.ResponseWriter, request *http.Request) {
|
||||
tmpl, err := template.New("index.html").ParseFiles("index.html")
|
||||
if err != nil {
|
||||
http.Error(writer, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
session, err := json.Marshal(getSession(request.Context()))
|
||||
if err != nil {
|
||||
http.Error(writer, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
err = tmpl.ExecuteTemplate(writer, "index.html", string(session))
|
||||
if err != nil {
|
||||
http.Error(writer, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
15
internal/oauth/oauth.go
Normal file
15
internal/oauth/oauth.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package oauth
|
||||
|
||||
type Client struct {
|
||||
Id string
|
||||
Secret string
|
||||
Issuer string
|
||||
}
|
||||
|
||||
func NewClient() *Client {
|
||||
return &Client{
|
||||
Id: "",
|
||||
Secret: "",
|
||||
Issuer: "",
|
||||
}
|
||||
}
|
||||
BIN
internal/oidc/oidc-auth
Executable file
BIN
internal/oidc/oidc-auth
Executable file
Binary file not shown.
|
|
@ -7,7 +7,7 @@ type OpenIDConnectProvider struct {
|
|||
ConfigEndpoint string
|
||||
}
|
||||
|
||||
func NewOpenIDConnect() *OpenIDConnectProvider {
|
||||
func NewOIDCProvider() *OpenIDConnectProvider {
|
||||
return &OpenIDConnectProvider{
|
||||
Host: "https://gitlab.newmexicoconsortium.org",
|
||||
AuthorizeEndpoint: "/oauth/authorize",
|
||||
|
|
@ -15,11 +15,11 @@ func NewOpenIDConnect() *OpenIDConnectProvider {
|
|||
}
|
||||
}
|
||||
|
||||
func (oidc *OpenIDConnectProvider) AuthorizeUrl() string {
|
||||
func (oidc *OpenIDConnectProvider) GetAuthorizeUrl() string {
|
||||
return oidc.Host + oidc.AuthorizeEndpoint
|
||||
}
|
||||
|
||||
func (oidc *OpenIDConnectProvider) TokenUrl() string {
|
||||
func (oidc *OpenIDConnectProvider) GetTokenUrl() string {
|
||||
return oidc.Host + oidc.TokenEndpoint
|
||||
}
|
||||
|
||||
16
internal/server/server.go
Normal file
16
internal/server/server.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func Start(host string, port int) error {
|
||||
http.HandleFunc("/oauth/callback", getAuthorizationCode)
|
||||
err := http.ListenAndServe(host+":"+fmt.Sprintf("%d", port), nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func getAuthorizationCode(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Printf("response from OIDC provider: %v\n", r)
|
||||
}
|
||||
39
internal/util/util.go
Normal file
39
internal/util/util.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
const (
|
||||
letterIdxBits = 6 // 6 bits to represent a letter index
|
||||
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
||||
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
||||
)
|
||||
|
||||
func RandomString(n int) string {
|
||||
b := make([]byte, n)
|
||||
// A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
|
||||
for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 0; {
|
||||
if remain == 0 {
|
||||
cache, remain = rand.Int63(), letterIdxMax
|
||||
}
|
||||
if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
|
||||
b[i] = letterBytes[idx]
|
||||
i--
|
||||
}
|
||||
cache >>= letterIdxBits
|
||||
remain--
|
||||
}
|
||||
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func BuildAuthorizationUrl(authEndpoint string, clientId string, redirectUri []string, state string, responseType string, scope []string) string {
|
||||
return authEndpoint + "?" + "cilent_id=" + clientId +
|
||||
"&redirect_url=" + strings.Join(redirectUri, ",") +
|
||||
"&response_type=" + responseType +
|
||||
"&state=" + state +
|
||||
"&scope=" + strings.Join(scope, "+")
|
||||
}
|
||||
28
main.go
28
main.go
|
|
@ -1,35 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"davidallendj/ochami-auth/oidc"
|
||||
"strings"
|
||||
)
|
||||
import "davidallendj/oidc-auth/cmd"
|
||||
|
||||
var (
|
||||
clientId = ""
|
||||
redirectUri = ""
|
||||
state = ""
|
||||
response_type = "code"
|
||||
userDB = ""
|
||||
)
|
||||
|
||||
func buildAuthorizationUrl(authEndpoint string, clientId string, redirectUri []string, state string, responseType string, scope []string) string {
|
||||
return authEndpoint + "?" + "cilent_id=" + clientId +
|
||||
"&redirect_url=" + strings.Join(redirectUri, ",") +
|
||||
"&response_type=" + responseType +
|
||||
"&state=" + state +
|
||||
"&scope=" + strings.Join(scope, "+")
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
client := oidc.NewOpenIDConnect()
|
||||
var authorizationUrl = buildAuthorizationUrl(
|
||||
client.
|
||||
)
|
||||
var tokenUrl = loginHost + tokenEndpoint
|
||||
// start a HTTP server to listen for callback responses
|
||||
// extract code from response and exchange for bearer token
|
||||
// extract ID token and save user info
|
||||
// use ID token/user info to get access token from Hydra
|
||||
cmd.Execute()
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue