Skip to content

Commit

Permalink
fix: favor packr2 over pkger
Browse files Browse the repository at this point in the history
  • Loading branch information
aeneasr committed Aug 25, 2020
1 parent ef55e50 commit ac18a45
Show file tree
Hide file tree
Showing 11 changed files with 184 additions and 73 deletions.
2 changes: 1 addition & 1 deletion .docker/Dockerfile
Expand Up @@ -8,7 +8,7 @@ RUN apk add -U --no-cache ca-certificates

COPY kratos /usr/bin/kratos

# Exposing the ory home directory to simplify passing in Kratos configuration (e.g. if the file $HOME/.kratos.yaml
# Exposing the ory home directory to simplify passing in Kratos configuration (e.g. if the file $HOME/.kratos.yaml
# exists, it will be automatically used as the configuration file).
VOLUME /home/ory

Expand Down
10 changes: 6 additions & 4 deletions .docker/Dockerfile-build
Expand Up @@ -7,14 +7,16 @@ WORKDIR /go/src/github.com/ory/kratos
ADD go.mod go.mod
ADD go.sum go.sum

ENV GO111MODULE on
ENV CGO_ENABLED 1

RUN go mod download
RUN GO111MODULE=on go install github.com/gobuffalo/packr/v2/packr2 github.com/markbates/pkger/cmd/pkger
RUN go install github.com/gobuffalo/packr/v2/packr2

ADD . .

RUN packr2
RUN pkger
RUN CGO_ENABLED=1 go build -tags sqlite -a -o /usr/bin/kratos
RUN go build -tags sqlite -a -o /usr/bin/kratos

FROM alpine:3.11

Expand All @@ -30,7 +32,7 @@ RUN mkdir -p /var/lib/sqlite
RUN chown ory:ory /var/lib/sqlite
VOLUME /var/lib/sqlite

# Exposing the ory home directory to simplify passing in Kratos configuration (e.g. if the file $HOME/.kratos.yaml
# Exposing the ory home directory to simplify passing in Kratos configuration (e.g. if the file $HOME/.kratos.yaml
# exists, it will be automatically used as the configuration file).
VOLUME /home/ory

Expand Down
3 changes: 1 addition & 2 deletions .goreleaser.yml
Expand Up @@ -8,9 +8,8 @@ env:
before:
hooks:
- go mod download
- go install github.com/gobuffalo/packr/v2/packr2 github.com/markbates/pkger/cmd/pkger
- go install github.com/gobuffalo/packr/v2/packr2
- packr2
- pkger

builds:
-
Expand Down
1 change: 0 additions & 1 deletion Makefile
Expand Up @@ -14,7 +14,6 @@ GO_DEPENDENCIES = github.com/ory/go-acc \
github.com/go-swagger/go-swagger/cmd/swagger \
golang.org/x/tools/cmd/goimports \
github.com/mikefarah/yq \
github.com/markbates/pkger/cmd/pkger \
github.com/gobuffalo/packr/v2/packr2

define make-go-dependency
Expand Down
181 changes: 155 additions & 26 deletions docs/docs/self-service.mdx
Expand Up @@ -4,6 +4,9 @@ title: Self-Service Flows
sidebar_label: Concepts and Overview
---

import Tabs from '@theme/Tabs'
import TabItem from '@theme/TabItem'

ORY Kratos implements flows that users perform themselves as opposed to
administrative intervention. Facebook and Google both provide self-service
registration and profile management features as you are able to make changes to
Expand Down Expand Up @@ -40,15 +43,15 @@ Research, Troy Hunt, ...) and implements the following flows:
- [User-Facing Error](self-service/flows/user-facing-errors)
- [2FA / MFA](self-service/flows/2fa-mfa-multi-factor-authentication)

Some flows break down into strategies which implement some of the flow's
Some flows break down into "flow methods" which implement some of the flow's
business logic:

- The `password` strategy implement the
[login and registration with "email or/and username and password" method](self-service/flows/user-login-user-registration/username-email-password),
account recovery flow ("reset your password"), and
and
["change your password" user settings method](self-service/flows/user-settings/change-password).
- The `oidc` (OpenID Connect, OAuth2, Social Sign In) strategy implements
["Sign in with ..." login and registration method](self-service/flows/user-login-user-registration/openid-connect-social-sign-in-oauth2),
["Sign in with ..." login and registration method](self-service/flows/user-login-user-registration/openid-connect-social-sign-in-oauth2)
and
["un/link another social account" user settings method](self-service/flows/user-settings/link-unlink-openid-connect-oauth2).
- The `profile` strategy implements the
Expand All @@ -75,44 +78,170 @@ All Self-Service Flows
[Profile Management](self-service/flows/user-settings),
[Account Recovery](self-service/flows/account-recovery),
[Email or Phone verification](self-service/flows/verify-email-account-activation))
support these two flow types and work more or less the same.
support these two flow types and use the same data models but do use different API endpoints.

### Browser Flows

1. The Browser opens (e.g. with `<a>` or `window.location.href`) the flow's
initialization endpoint (e.g.`/self-service/login/browser` or
`/self-service/registration/browser`).
2. The initialization endpoint creates a flow object and stores it in the
database. The flow object has an ID and contains additional information about
the flow such as the login methods (e.g. "username/password" and "Sign in
with Google") and their form data. Once stored, the Browser is HTTP 302
redirected to the flow's configured UI URL (e.g.
`selfservice.flows.login.ui_url`), appending the flow ID as the `flow` URL
Query Parameter;
3. The endpoint responsible for the UI URL uses the `flow` URL Query Parameter
(e.g. `http://my-app/auth/login?flow=abcde`) to call the flow information
endpoint (e.g. `/self-service/login/flows?id=abcde`) and fetch the flow
data - so for example the login form and any validation errors. This endpoint
is available at both ORY Kratos's Public and Admin Endpoints.
4. The UI endpoint renders the fetched data in any way it sees it fit. The flow
is typically completed by the browser making another request to one of ORY
Kratos' endpoints, which is usually described in the fetched request data in
form of an e.g. form action URL.
First, the Browser opens the flow's initialization endpoint (e.g.`/self-service/login/browser`,
`/self-service/registration/browser`, ...):

<Tabs
defaultValue="curl"
values={[
{label: 'CURL', value: 'curl'},
{label: 'HTML', value: 'html'},
{label: 'Javascript', value: 'js'},
{label: 'ExpressJS', value: 'nodejs'},
]}>
<TabItem value="curl">

This CURL request emulates a browser request:

```
$ curl -x GET
-H "Accept: text/html" http://127.0.0.1:4433/self-service/login/browser
```

</TabItem>
<TabItem value="html">

```html
<a href="http://127.0.0.1:4433/self-service/login/browser">Sign in</a>
```

</TabItem>
<TabItem value="js">

```html
<script>
const initLogin = () => {
window.location.href = 'http://127.0.0.1:4433/self-service/login/browser'
}
</script>

<a onClick="initLogin">Sign in</a>
```

</TabItem>
<TabItem value="nodejs">

```js
express.get('/login', function (req, res) {
res.redirect(302, 'http://127.0.0.1:4433/')
})

```

</TabItem>
</Tabs>

The initialization endpoint creates a flow object and stores it in the
database. The flow object has an ID and contains additional information about
the flow such as the login methods (e.g. "username/password" and "Sign in
with Google") and their form data.

Once stored, the Browser is HTTP 302 redirected to the flow's configured UI URL (e.g.
`selfservice.flows.login.ui_url`), appending the flow ID as the `flow` URL
Query Parameter. The initialization endpoint (e.g. `http://127.0.0.1:4433/self-service/login/browser`)
responsds with:

```
GET http(s)://public.my-kratos/self-service/login/browser
HTTP 302 Found
Location:
```

The Browser opens the URL which renders the HTML form that, for example, shows the "username and password" field,
the "Update your email address" field, or other flow forms. This HTML form is rendered be the [Self-Service UI](concepts/ui-user-interface#self-service-user-interface-ssui)
which you fully control.

The endpoint responsible for the UI URL uses the `flow` URL Query Parameter
(e.g. `http://my-app/auth/login?flow=abcde`) to call the flow information
endpoint (e.g. `/self-service/login/flows?id=abcde`) and fetch the flow
data - so for example the login form and any validation errors. This endpoint
is available at both ORY Kratos's Public and Admin Endpoints. For example,
the Self-Service UI responsible for rendering the Login HTML Form would
make a request along the lines of:

```
# The endpoint uses ORY Kratos' REST API to fetch information about the request
$ curl -x GET \
-H "Accept: application/json" \
http://127.0.0.1:4433/self-service/login/browser?request=...
```

The result includes login methods, their fields, and additional information about the flow:

```
```

For more details, check out the individual flow documentation.

The user completes the flow by submitting the form. The form action always points to ORY Kratos directly.
The business logic for the flow method will then validate the form payload and return to the UI URL on
validation errors

and include the validation errors.

If a system error (e.g. broken configuration file) occurs, the browser is redirected to the
[Error UI](self-service/flows/user-facing-errors).

If the form payload is valid, the flow completes with a success. The result here depends on the flow itself -
the login flow for example redirects the user to a specified redirect URL and sets a session cookie. The
registration flow also redirects to a specified redirect URL but only creates the user in the database and might
issue a session cookie if configured to do so.

### API Flows

1. The client makes a `Accept: application/json` GET request to the flow's
initialization endpoint (e.g.`/self-service/login/api` or
`/self-service/registration/api`).
The client (e.g. mobile application) makes a request to the flow's initialization endpoint


First, the Browser opens the flow's initialization endpoint (e.g.`/self-service/login/browser`,
`/self-service/registration/browser`, ...):

<Tabs
defaultValue="curl"
values={[
{label: 'CURL', value: 'curl'},
{label: 'NodeJS', value: 'nodejs'},
]}>
<TabItem value="curl">
```
$ curl -x GET \
-H "Accept: application/json" \
http://127.0.0.1:4433/self-service/login/api
```

</TabItem>
<TabItem value="nodejs">

```js
const fetch = require('node-fetch')

fetch.get('http://127.0.0.1:4433/self-service/login/api', {
headers: { Accept: 'text/html' }
})
.then(res=>res.json())
.then((flow) => console.log(flow))
```

</TabItem>
</Tabs>

2. The initialization endpoint creates a flow object and stores it in the
database. The flow object has an ID and contains additional information about
the flow such as the login methods (e.g. "username/password" and "Sign in
with Google") and their form data. Once stored, the flow data is directly
returned as `application/json` without redirects.

3. The client uses the flow data to render the UI - so for example the login
form and any validation errors.

4. The client completes the flow by e.g. making another `application/json`
request.

5. If needed, the client can re-request the flow information (e.g. because the
form completed with validation errors) by calling the flow information
endpoint (e.g. `/self-service/login/flows?id=abcde`). This endpoint is
Expand Down
1 change: 0 additions & 1 deletion go.mod
Expand Up @@ -46,7 +46,6 @@ require (
github.com/julienschmidt/httprouter v1.2.0
github.com/justinas/nosurf v1.1.0
github.com/leodido/go-urn v1.1.0 // indirect
github.com/markbates/pkger v0.17.0
github.com/mattn/goveralls v0.0.5
github.com/mikefarah/yq v1.15.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
Expand Down
2 changes: 0 additions & 2 deletions go_mod_indirect_pins.go
Expand Up @@ -24,7 +24,5 @@ import (

_ "github.com/hashicorp/consul/api"

_ "github.com/markbates/pkger/cmd/pkger"

_ "github.com/ory/cli"
)
4 changes: 0 additions & 4 deletions main.go
Expand Up @@ -16,8 +16,6 @@
package main

import (
"github.com/markbates/pkger"

"github.com/ory/x/profilex"

"github.com/ory/kratos/cmd"
Expand All @@ -26,7 +24,5 @@ import (
func main() {
defer profilex.Profile().Stop()

_ = pkger.Include("/courier/template/templates")
_ = pkger.Include("/.schema/config.schema.json")
cmd.Execute()
}
21 changes: 16 additions & 5 deletions selfservice/strategy/password/schema.go
@@ -1,10 +1,21 @@
package password

import (
"github.com/markbates/pkger"

"github.com/ory/kratos/x/pkgerx"
"github.com/gobuffalo/packr/v2"
)

var loginSchema = pkgerx.MustRead(pkger.Open("/selfservice/strategy/password/.schema/login.schema.json"))
var registrationSchema = pkgerx.MustRead(pkger.Open("/selfservice/strategy/password/.schema/registration.schema.json"))
var schemas = packr.New(".schema", ".schema")
var loginSchema, registrationSchema []byte

func init() {
var err error
loginSchema, err = schemas.Find("login.schema.json")
if err != nil {
panic(err)
}

registrationSchema, err = schemas.Find("registration.schema.json")
if err != nil {
panic(err)
}
}
15 changes: 5 additions & 10 deletions x/config.go
@@ -1,23 +1,18 @@
package x

import (
"io/ioutil"

"github.com/markbates/pkger"

"github.com/gobuffalo/packr/v2"
"github.com/ory/x/logrusx"
"github.com/ory/x/viperx"
)

var schemas = packr.New("schemas", "../.schema")

func WatchAndValidateViper(log *logrusx.Logger) {
file, err := pkger.Open("/.schema/config.schema.json")
schema, err := schemas.Find("config.schema.json")
if err != nil {
log.WithError(err).Fatal("Unable to open configuration JSON Schema.")
}
defer file.Close()
schema, err := ioutil.ReadAll(file)
if err != nil {
log.WithError(err).Fatal("Unable to read configuration JSON Schema.")
}

viperx.WatchAndValidateViper(log, schema, "ORY Kratos", []string{"serve", "profiling", "log"}, "")
}
17 changes: 0 additions & 17 deletions x/pkgerx/pkgerx.go

This file was deleted.

0 comments on commit ac18a45

Please sign in to comment.