Skip to content

Commit

Permalink
implement JWT validation (support for HMAC/RSA)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex Sch committed Aug 25, 2020
1 parent 1c1266f commit 88ac7df
Show file tree
Hide file tree
Showing 44 changed files with 3,484 additions and 0 deletions.
17 changes: 17 additions & 0 deletions examples/jwt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Functions

This example uses the functions and HTTP implementations to call the API endpoints at [jsonplaceholder.typicode.com](https://jsonplaceholder.typicode.com/).

# Getting started

You could get started by executing the `main.go` file.

```bash
$ go run main.go
```

Once Semaphore is up and running you could execute the `todo` flow by calling the service on port `8080`.

```bash
$ curl 127.0.0.1:8080/ -H 'Authorization: super-secret'
```
51 changes: 51 additions & 0 deletions examples/jwt/flow.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
endpoint "FetchLatestProject" "http" {
endpoint = "/"
method = "GET"
codec = "json"
}

error "proto.Error" {
value = "some message"
status = 400
}

flow "FetchLatestProject" {
input "proto.Query" {
header = ["Authorization"]
}

error "proto.Unauthorized" {
message = "{{ error:message }}"
status = "{{ error:status }}"
}

on_error {
status = 401
message = "on error message"
}

before {
resources {
claims = "{{ jwt(input.header:Authorization) }}"
}
}

resource "query" {
request "proto.Service" "GetTodo" {}
}

resource "user" {
request "proto.Service" "GetUser" {}
}

output "proto.Item" {
header {
Username = "{{ user:username }}"
}

id = "{{ query:id }}"
title = "{{ query:title }}"
completed = "{{ query:completed }}"
claimer_id = "{{ claims:subject }}"
}
}
69 changes: 69 additions & 0 deletions examples/jwt/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package main

import (
dgrijalva "github.com/dgrijalva/jwt-go"
"github.com/jexia/semaphore"
"github.com/jexia/semaphore/cmd/semaphore/daemon"
"github.com/jexia/semaphore/cmd/semaphore/daemon/providers"
"github.com/jexia/semaphore/pkg/broker"
"github.com/jexia/semaphore/pkg/broker/logger"
"github.com/jexia/semaphore/pkg/codec/json"
"github.com/jexia/semaphore/pkg/codec/proto"
"github.com/jexia/semaphore/pkg/functions"
"github.com/jexia/semaphore/pkg/functions/lib/jwt"
"github.com/jexia/semaphore/pkg/providers/hcl"
"github.com/jexia/semaphore/pkg/providers/protobuffers"
"github.com/jexia/semaphore/pkg/transport/http"
)

// Claims is a custom implementation of lib/jwt.Claims interface.
type Claims struct{ dgrijalva.StandardClaims }

// Subject is a method returning subject (to satisfy Claims interface).
func (c Claims) Subject() string { return c.StandardClaims.Subject }

// NewClaims instantiates a new claims object.
func NewClaims() jwt.Claims { return new(Claims) }

func main() {
var (
reader = jwt.HMAC("very-strong-secret")
ctx = logger.WithLogger(broker.NewContext())
functions = functions.Custom{
"jwt": jwt.JWT(reader, NewClaims),
}
)

core, err := semaphore.NewOptions(ctx,
semaphore.WithLogLevel("*", "debug"),
semaphore.WithFlows(hcl.FlowsResolver("./*.hcl")),
semaphore.WithCodec(json.NewConstructor()),
semaphore.WithCodec(proto.NewConstructor()),
semaphore.WithCaller(http.NewCaller()),
semaphore.WithFunctions(functions),
)

if err != nil {
panic(err)
}

options, err := providers.NewOptions(ctx, core,
providers.WithEndpoints(hcl.EndpointsResolver("./*.hcl")),
providers.WithSchema(protobuffers.SchemaResolver([]string{"./proto"}, "./proto/*.proto")),
providers.WithServices(protobuffers.ServiceResolver([]string{"./proto"}, "./proto/*.proto")),
providers.WithListener(http.NewListener(":8080")),
)

if err != nil {
panic(err)
}

client, err := daemon.NewClient(ctx, core, options)
if err != nil {
panic(err)
}

if err := client.Serve(); err != nil {
panic(err)
}
}
65 changes: 65 additions & 0 deletions examples/jwt/proto/api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# API

This package contains API artifacts such as protobuf annotations.

## Protobuf Usage

1. Define your [gRPC](https://grpc.io/docs/) service using protocol buffers

`your_service.proto`:
```protobuf
syntax = "proto3";
package example;
message StringMessage {
string value = 1;
}
service YourService {
rpc Echo(StringMessage) returns (StringMessage) {}
}
```

2. Add a [`semaphore.api`](https://github.com/jexia/semaphore/blob/master/api/annotations.proto)
annotation to your .proto file

`your_service.proto`:
```diff
syntax = "proto3";
package example;
+
+import "semaphore/api/annotations.proto";
+
message StringMessage {
string value = 1;
}

service YourService {
+ option (semaphore.api.service) = {
+ host: "127.0.0.1:80"
+ transport: "http"
+ codec: "json"
+ };
+
- rpc Echo(StringMessage) returns (StringMessage) {}
+ rpc Echo(StringMessage) returns (StringMessage) {
+ option (semaphore.api.http) = {
+ post: "/v1/example/echo"
+ body: "*"
+ };
+ }
}
```

>You will need to provide the required third party protobuf files to the `protoc` compiler.
>They are included in this repo under the `api` folder, and we recommend copying
>them into your `protoc` generation file structure. If you've structured your protofiles according
>to something like [the Buf style guide](https://buf.build/docs/style-guide#files-and-packages),
>you could copy the files into a top-level `./semaphore` folder.
If you do not want to modify the proto file for use with grpc-gateway you can
alternatively use an external
[Service Configuration](https://github.com/jexia/semaphore/tree/master/cmd/semaphore/config) file.
[Check our documentation](https://jexia.gitbook.io/semaphore/getting-started/cli)
for more information.

3. Write flow your definitions as usual
Loading

0 comments on commit 88ac7df

Please sign in to comment.