Migrating from IHostingEnvironment to IWebHostEnvironment - f#

I have this:
let configureApp (app : IApplicationBuilder) =
let env = app.ApplicationServices.GetService<IHostingEnvironment>()
(match env.IsDevelopment() with
| true -> app.UseDeveloperExceptionPage()
| false -> app.UseGiraffeErrorHandler errorHandler)
And when I tried to migrate, I get the error:
let configureApp (app : IApplicationBuilder) =
let env = app.ApplicationServices.GetService<IWebHostEnvironment>()
(match env. with
I do not know what I should change in this code snippet.

Based on the documentation, you need to make sure that you have the namespace Microsoft.AspNetCore.Hosting open.

Related

unauthorized error when using JWT in Giraffe, F#

I am using JWT(System.IdentityModel.Tokens.Jwt) for authentication in Giraffe F#. I am successfully getting the JWT token, but when I use it on my API/tables route I get a 401 unauthorized response. I have tried to change the issuer and audience to http://localhost:5001 and http://localhost:5000 respectively that didn't work then I disabled ValidateAudience but didn't work either.
Here is my route:
open System
open Microsoft.AspNetCore.Builder
open Microsoft.AspNetCore.Cors.Infrastructure
open Microsoft.AspNetCore.Hosting
open Microsoft.Extensions.Hosting
open Microsoft.Extensions.Logging
open Microsoft.Extensions.DependencyInjection
open Giraffe
open backend.HttpHandlers
open System.Text
open Microsoft.IdentityModel.Tokens
open Microsoft.AspNetCore.Authentication.JwtBearer
open Microsoft.AspNetCore.Http
open JwtAuth
let authorize : HttpFunc -> HttpContext -> HttpFuncResult =
requiresAuthentication (challenge JwtBearerDefaults.AuthenticationScheme)
let webApp =
choose [ subRoute
"/api"
(choose [ POST
>=> choose [ route "/token" >=> Auth.handlePostToken
route "/user/add" >=> handleAddUser
route "/table/create">=> handleAddTable
route "/table/addData">=> authorize>=> handleAddTableData ]
GET
>=> choose [
route "/tables">=> authorize>=> handleGetTableNames ] ])
setStatusCode 404 >=> text "Not Found" ]
// ---------------------------------
// Error handler
// ---------------------------------
let errorHandler (ex: Exception) (logger: ILogger) =
logger.LogError(ex, "An unhandled exception has occurred while executing the request.")
clearResponse
>=> setStatusCode 500
>=> text ex.Message
Here is my config:
// ---------------------------------
// Config and Main
// ---------------------------------
let configureCors (builder: CorsPolicyBuilder) =
builder
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
|> ignore
let configureApp (app: IApplicationBuilder) =
let env =
app.ApplicationServices.GetService<IWebHostEnvironment>()
(match env.IsDevelopment() with
| true -> app.UseDeveloperExceptionPage()
| false ->
app.UseCors(configureCors)
.UseAuthentication()
.UseGiraffeErrorHandler(errorHandler)
.UseHttpsRedirection())
.UseGiraffe(webApp)
let configureServices (services: IServiceCollection) =
let sp = services.BuildServiceProvider()
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(fun options ->
options.TokenValidationParameters <-
TokenValidationParameters(
ValidateActor = false,
ValidateAudience = false,
ValidateLifetime = false,
ValidateIssuerSigningKey = false,
ValidIssuer = "http://localhost:5001",
ValidAudience = "http://localhost:5000",
IssuerSigningKey = SymmetricSecurityKey(Encoding.UTF8.GetBytes(Auth.secret))
))|> ignore
services.AddCors() |> ignore
services.AddGiraffe() |> ignore
let configureLogging (builder: ILoggingBuilder) =
builder.AddConsole().AddDebug() |> ignore
[<EntryPoint>]
let main args =
Host
.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(fun webHostBuilder ->
webHostBuilder
.Configure(Action<IApplicationBuilder> configureApp)
.ConfigureServices(configureServices)
.ConfigureLogging(configureLogging)
|> ignore)
.Build()
.Run()
0
Here is the function I use to generate the token:
let secret = "spadR2dre#u-ruBrE#TepA&*Uf#U"
let generateToken email =
let claims = [|
Claim(JwtRegisteredClaimNames.Sub, email);
Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) |]
let expires = Nullable(DateTime.UtcNow.AddHours(1.0))
let notBefore = Nullable(DateTime.UtcNow)
let securityKey = SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret))
let signingCredentials = SigningCredentials(key = securityKey, algorithm = SecurityAlgorithms.HmacSha256)
let token =
JwtSecurityToken(
issuer = "http://localhost:5001",
audience = "http://localhost:5000",
claims = claims,
expires = expires,
notBefore = notBefore,
signingCredentials = signingCredentials)
let tokenResult = {
Token = JwtSecurityTokenHandler().WriteToken(token)
}
tokenResult
I am using thunder client vscode to make the requests.

how to redirect from http to https with suave, in f#

How can I redirect a connection from http to https using Suave?
at https://gist.github.com/ademar/f4ddb788162dbdd9e104574e2accf07f I found this:
let redirectToSsl : WebPart =
context(fun c ->
match c.request.header "x-forwarded-proto" with
| Choice1Of2 "http" ->
let uriBuilder = new UriBuilder(
Scheme = Uri.UriSchemeHttps,
Path = c.request.path,
Host = c.request.host)
Redirection.redirect (uriBuilder.Uri.ToString())
| _ -> fun _ ->async { return None })
but I am not really sure where that would fit in the pipeline?
I would change two things:
Check for the actual protocol. I think that x-forwarded-proto is only used for proxies, but I'm not certain.
To fit it into your pipeline, accept a webpart to invoke when the access is secure.
Result looks like this:
let redirectToSsl allow : WebPart =
context (fun c ->
if c.request.binding.scheme.secure then
allow
else
let uriBuilder =
new UriBuilder(
Scheme = Uri.UriSchemeHttps,
Path = c.request.path,
Host = c.request.host)
Redirection.redirect (uriBuilder.Uri.ToString()))
Usage looks like this:
let app = redirectToSsl Files.browseHome // allow browsing under SSL only
Caveat: I haven't tried this in practice, so there could be other issues I'm overlooking.

WebPart for forwarding requests to another server using Suave?

I would like to create a WebPart that forwards all requests to another web server that I specify.
Usage might look like this:
let app =
choose
[
path "/" >=> OK "Hello, world. "
path "/graphql" >=> createProxy "localhost:5000"
RequestErrors.NOT_FOUND "Not found"
]
startWebServer defaultConfig app
How should I implement this in Suave?
I found this snippet, but it seems to be for an old version of Suave:
let build_proxy_resolver (fwd_to_host : String) fwd_to_port =
let heserver = System.Net.Dns.GetHostEntry(fwd_to_host)
let ipaddr = heserver.AddressList.[0]
fun (request : HttpRequest) ->
Some (ipaddr, fwd_to_port)
let build_headers ctx =
//add and remove headers from the ctx, return the header list
ctx.request.headers
let proxy_app (ctx:HttpContext) =
let new_headers = build_headers ctx
let fwd_ctx = {ctx with request={ctx.request with headers=new_headers}}
let pxy = proxy (build_proxy_resolver "PROXY_TO.com" 80us) fwd_ctx
{ctx with response = { ctx.response with status=Suave.Types.Codes.HTTP_200; content=SocketTask pxy }} |> Some
This has now been added to Suave:
open System
open Suave
open Suave.Proxy
let app =
choose
[
path "/" >=> OK "Hello, world. "
path "/graphql" >=> proxy (Uri "http://localhost:5000")
RequestErrors.NOT_FOUND "Not found"
]
startWebServer defaultConfig app

APPID and CLIENTKEY in ios apps after deploying parse server to AWS

I deployed a parse server to AWS Elastic Beanstalk and rewrite my iOS app in AppDelegate.swift. But i only rewrited the ParseMutableClientConfiguration.server so, the APPID and CLIENTKEY is still the same ID and KEYS that are in parse.api.com
It this a correct way to configure my ID and KEYS and server? or do i need to generate a new APPID by myself in this case?
let config = ParseClientConfiguration(block: {
(ParseMutableClientConfiguration) -> Void in
ParseMutableClientConfiguration.applicationId = APP_ID;
ParseMutableClientConfiguration.clientKey = CLIENT_ID;
ParseMutableClientConfiguration.server = "EC2 DNS"
});
Parse.initializeWithConfiguration(config)
Yes this is fine. With swift 3 its done like this.
// Init Parse
let configuration = ParseClientConfiguration {
$0.applicationId = "XXX"
$0.clientKey = "XXX"
$0.server = "XXX"
$0.isLocalDatastoreEnabled = true
}
Parse.initialize(with: configuration)

Mounting Suave in an Owin application

I have an existing owin application written in C# and would like to mount a suave application as a middleware but since I am relatively new to F# I am finding it quite difficult to navigate how this should be done. I think I'm looking for something like:
// in F# land
module MySuaveApp.ApiModule
let app =
choose
[ GET >=> choose
[ path "/hello" >=> OK "Hello GET"
path "/goodbye" >=> OK "Good bye GET" ]
POST >=> choose
[ path "/hello" >=> OK "Hello POST"
path "/goodbye" >=> OK "Good bye POST" ] ]
let getSuaveAsMiddleware() =
... magic goes here ...
// in Startup.cs
app.Use(MySuaveApp.ApiModule.getSuaveAsMiddleware())
As for what that magic should be I think it's a combination of OwinApp.ofAppFunc or OwinApp.ofMidFunc, but I can't for the life of me figure out what it should be.
There is no easy magic.1 ofAppFunc and ofMidFunc are here for creating WebParts out of OWIN components, i.e. OWIN -> Suave, whereas you want Suave -> OWIN.
The following works for your 'application' and serves as an example what would be needed to get it working:
open System.Runtime.CompilerServices
[<Extension>]
module Api =
open Suave
open Successful
open Filters
open Operators
open Microsoft.Owin
open System.Threading.Tasks
let app =
choose [ GET >=> choose [ path "/hello" >=> OK "Hello GET"
path "/goodbye" >=> OK "Good bye GET" ]
POST >=> choose [ path "/hello" >=> OK "Hello POST"
path "/goodbye" >=> OK "Good bye POST" ] ]
let withCtx (ctx : IOwinContext) webpart =
async {
let request =
{ HttpRequest.empty with
headers = ctx.Request.Headers |> List.ofSeq |> List.map (fun kvp -> kvp.Key, kvp.Value |> String.concat ",")
host = ctx.Request.Host.Value
``method`` = HttpMethod.parse ctx.Request.Method
url = ctx.Request.Uri }
let! res = webpart { HttpContext.empty with request = request }
res |> Option.iter (fun r ->
ctx.Response.StatusCode <- r.response.status.code
match r.response.content with
| Bytes bs -> ctx.Response.Write bs
| _ -> failwith "Not supported")
return res
}
type SuaveMiddleware(n) =
inherit OwinMiddleware(n)
override __.Invoke(context : IOwinContext) =
let res = withCtx context app |> Async.RunSynchronously
match res with
| Some _ -> Task.CompletedTask
| None -> base.Next.Invoke context
[<Extension>]
let UseSuave(app : Owin.IAppBuilder) =
app.Use(typeof<SuaveMiddleware>)
The main works is delegated to withCtx that tries to fulfill a request given a IOwinContext and a WebPart. It does so mainly by converting back and forth between Suave and OWIN context and related entities.
Note that this code is a PoC (Proof-of-Concept) and not fit for production.
The SuaveMiddleware forwards request to the next middleware if Suave cannot fulfill the request.
Using from C# is easy then:
using MySuave;
using Owin;
namespace Main
{
using System.Web.Http;
public class Startup
{
public static void Configuration(IAppBuilder appBuilder)
{
appBuilder.UseSuave();
var config = new HttpConfiguration();
config.MapHttpAttributeRoutes();
appBuilder.UseWebApi(config);
}
}
}
given
namespace Main.Example
{
using System.Web.Http;
[RoutePrefix("api")]
public class ExampleController : ApiController
{
[HttpGet, Route("")]
public string Index()
{
return "Hello World";
}
}
}
And both URLs work:
http://localhost:9000/hello
Hello GET
http://localhost:9000/api
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Hello World</string>
1 At least none I know of. I'm happy to be proven wrong.

Resources