Skip to content

OAuth2 Security Schema Code Generation  #100

@ChrisWellsWood

Description

@ChrisWellsWood

Many thanks for all your hard work on elm-open-api, it's much appreciated! Further to our discussion on Elm Discourse, here's information regarding the problem and the solution I've implemented.

Description

When trying to generate code for an API that uses OAuth2 for security, the following error is generated:

Warning: Todo: Unsupported security schema: Oauth2
  at /users/me/ -> GET

To Reproduce

To reproduce this, add the following to the security section of your openapi.json schema file:

        "securitySchemes": {
            "OAuth2PasswordBearer": {
                "type": "oauth2",
                "flows": {
                    "password": {
                        "scopes": {},
                        "tokenUrl": "token"
                    }
                }
            }
        }

Additionally, here is the full openapi.json file that I'm currently using:

Full openapi.json file
{
    "openapi": "3.1.0",
    "info": {
        "title": "Paracosm API",
        "summary": "Web API for the `paracosm` framework.",
        "version": "0.1.0"
    },
    "servers": [
        {
            "url": "http://127.0.0.1:8000",
            "description": "Development environment"
        }
    ],
    "paths": {
        "/token": {
            "post": {
                "summary": "Log in to get access token for API through docs..",
                "operationId": "login_for_docs_token_post",
                "requestBody": {
                    "content": {
                        "application/x-www-form-urlencoded": {
                            "schema": {
                                "$ref": "#/components/schemas/Body_login_for_docs_token_post"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Token"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/json-token": {
            "post": {
                "summary": "Log in to get access token for API.",
                "operationId": "login_json_token_post",
                "parameters": [
                    {
                        "name": "username",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "title": "Username"
                        }
                    },
                    {
                        "name": "password",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "title": "Password"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Token"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/users/": {
            "post": {
                "summary": "Create a new user.",
                "operationId": "create_user_users__post",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/UserCreate"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UserPublic"
                                }
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/HTTPValidationError"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/users/me/": {
            "get": {
                "summary": "Get current user information.",
                "operationId": "me_users_me__get",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/UserPublic"
                                }
                            }
                        }
                    }
                },
                "security": [
                    {
                        "OAuth2PasswordBearer": []
                    }
                ]
            }
        }
    },
    "components": {
        "schemas": {
            "Body_login_for_docs_token_post": {
                "properties": {
                    "grant_type": {
                        "anyOf": [
                            {
                                "type": "string",
                                "pattern": "password"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Grant Type"
                    },
                    "username": {
                        "type": "string",
                        "title": "Username"
                    },
                    "password": {
                        "type": "string",
                        "title": "Password"
                    },
                    "scope": {
                        "type": "string",
                        "title": "Scope",
                        "default": ""
                    },
                    "client_id": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Client Id"
                    },
                    "client_secret": {
                        "anyOf": [
                            {
                                "type": "string"
                            },
                            {
                                "type": "null"
                            }
                        ],
                        "title": "Client Secret"
                    }
                },
                "type": "object",
                "required": [
                    "username",
                    "password"
                ],
                "title": "Body_login_for_docs_token_post"
            },
            "HTTPValidationError": {
                "properties": {
                    "detail": {
                        "items": {
                            "$ref": "#/components/schemas/ValidationError"
                        },
                        "type": "array",
                        "title": "Detail"
                    }
                },
                "type": "object",
                "title": "HTTPValidationError"
            },
            "Token": {
                "properties": {
                    "access_token": {
                        "type": "string",
                        "title": "Access Token"
                    },
                    "token_type": {
                        "type": "string",
                        "title": "Token Type"
                    }
                },
                "type": "object",
                "required": [
                    "access_token",
                    "token_type"
                ],
                "title": "Token"
            },
            "UserCreate": {
                "properties": {
                    "username": {
                        "type": "string",
                        "title": "Username"
                    },
                    "email": {
                        "type": "string",
                        "title": "Email"
                    },
                    "full_name": {
                        "type": "string",
                        "title": "Full Name"
                    },
                    "password": {
                        "type": "string",
                        "title": "Password"
                    },
                    "repeated_password": {
                        "type": "string",
                        "title": "Repeated Password"
                    }
                },
                "type": "object",
                "required": [
                    "username",
                    "email",
                    "full_name",
                    "password",
                    "repeated_password"
                ],
                "title": "UserCreate"
            },
            "UserPublic": {
                "properties": {
                    "username": {
                        "type": "string",
                        "title": "Username"
                    },
                    "email": {
                        "type": "string",
                        "title": "Email"
                    },
                    "full_name": {
                        "type": "string",
                        "title": "Full Name"
                    },
                    "id": {
                        "type": "integer",
                        "title": "Id"
                    }
                },
                "type": "object",
                "required": [
                    "username",
                    "email",
                    "full_name",
                    "id"
                ],
                "title": "UserPublic"
            },
            "ValidationError": {
                "properties": {
                    "loc": {
                        "items": {
                            "anyOf": [
                                {
                                    "type": "string"
                                },
                                {
                                    "type": "integer"
                                }
                            ]
                        },
                        "type": "array",
                        "title": "Location"
                    },
                    "msg": {
                        "type": "string",
                        "title": "Message"
                    },
                    "type": {
                        "type": "string",
                        "title": "Error Type"
                    }
                },
                "type": "object",
                "required": [
                    "loc",
                    "msg",
                    "type"
                ],
                "title": "ValidationError"
            }
        },
        "securitySchemes": {
            "OAuth2PasswordBearer": {
                "type": "oauth2",
                "flows": {
                    "password": {
                        "scopes": {},
                        "tokenUrl": "token"
                    }
                }
            }
        }
    }
}

I'm passing a json web token around for auth, and I can extend the code generated by elm-open-api relatively easily to enable authentication. For example, I can change the /users/me endpoint, which requires auth, from this:

meUsersMeGet :
    { toMsg :
        Result (OpenApi.Common.Error ParacosmApi.Types.MeUsersMeGet_Error String) ParacosmApi.Types.UserPublic
        -> msg
    }
    -> Cmd msg
meUsersMeGet config =
    Http.request
        { url = "http://127.0.0.1:8000/users/me/"
        , method = "GET"
        , headers = []
        , expect =
            OpenApi.Common.expectJsonCustom
                config.toMsg
                (Dict.fromList [])
                ParacosmApi.Json.decodeUserPublic
        , body = Http.emptyBody
        , timeout = Nothing
        , tracker = Nothing
        }

to this:

meUsersMeGet :
    { toMsg :
        Result (OpenApi.Common.Error ParacosmApi.Types.MeUsersMeGet_Error String) ParacosmApi.Types.UserPublic
        -> msg
    -- Next line changed
    , token : ParacosmApi.Types.Token 
    }
    -> Cmd msg
meUsersMeGet config =
    Http.request
        { url = "http://127.0.0.1:8000/users/me/"
        , method = "GET"
       -- Next line changed
        , headers = [ Http.header "Authorization" ("Bearer " ++ config.token.access_token) ]
        , expect =
            OpenApi.Common.expectJsonCustom
                config.toMsg
                (Dict.fromList [])
                ParacosmApi.Json.decodeUserPublic
        , body = Http.emptyBody
        , timeout = Nothing
        , tracker = Nothing
        }

And the API works as expected.

Additional Context

I am a novice when it comes to making OpenAPI compliant APIs, so there's a good chance some of the configuration is wrong on my end. I followed this section of the FastAPI docs to get to this point: https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions