How to integrate AngularJs with Django - angularjs

I have little/no knowdlege in AngularJs, i want to create a very simple SPA with django as backend and AngularJs as frontend. User will be able to Register, Login & logout all taking place on the same page when a user logs in they will see a message"Welcome (user email)".
This is very easy to do in a normal django web app as we don't even need to create the whole authentication system. But i want to learn how to do this with AngularJS as my employer wants me to do.
I have read the basics of AngularJs and it looked well explained (it made sense) but how to integrate it with django. I tried searching on the internet but there is nothing out there, the tutorials that are there are more then 7 years old.

First you need to look at angular HttpClient because that what going to enable communication between django and angular..then create a service to handle authentication you can call it anything import HttpClient inside to login user for example i will do this:
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Observable } from 'rxjs';
import { environment } from 'src/environments/environment';
import { Router } from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class AuthServiceService {
constructor(private http: HttpClient, private router: Router) { }
// Skip authorization from these requests
private skip_token_headers = {
skip: "true"
};
loginUser(user): Observable<any> {
return this.makePostRequest(environment.LOGIN_URL, user,
this.skip_token_headers);
}
makePostRequest(url: string, data: any, req_headers: any): Observable<any> {
return this.http.post(url, data, { headers: req_headers });
}
then in your login component under .ts you import your service and use it like bellow
onLoginSuccess(result){
localStorage.setItem('token', result.key);
localStorage.setItem('user', JSON.stringify(result.user));
this.router.navigate(['/userpage']);
}
onSubmit(form: NgForm) {
this.authService.loginUser(this.user).subscribe(
result => {
this.onLoginSuccess(result);
},
error => {
this.login_errors = [];
if (error['error']['non_field_errors']) {
this.login_errors = error['error']['non_field_errors'];
}
// this.clearErrors(this.user);
// this.showErrors(error);
}
);
}

Related

In Next.js 13 with turbopack, how do I access cookies without getServerSideProps?

I have an app that persists some values in a cookie. I know that there are other tools such as useState, useContext, etc... but this particular app works with a library that stores information in a jwt so I have to read certain values by fetching the jwt. I am porting the app from next.js 12 (with webpack) to next.js 13 (with turbopack).
I've already ported the app structurally to fit the app style routing of next.js 13. My pages all go in their individual folders with sub layouts WITHIN the app directory, and I have a master layout and homepage directly in the app directory.
The old code for my protected page in next.js 12 looked like this:
protected.tsx
import type { NextPage } from 'next';
import { GetServerSideProps } from 'next';
import { useContext } from 'react';
//#ts-ignore
import Cookies from 'cookies';
const Protected: NextPage = (props: any) => {
if (!props.authorized) {
return (
<h2>Unauthorized</h2>
)
} else {
return (
<div className="max-w-md">
<h1 className="font-bold">This is the Protected Section</h1>
</div>
)}
}
export const getServerSideProps: GetServerSideProps = async ({ req, res, query }) => {
const { id } = query
const cookies = new Cookies(req, res)
const jwt = cookies.get('<MY TOKEN NAME>')
if (!jwt) {
return {
props: {
authorized: false
},
}
}
const { verified } = <MY TOKEN SDK INSTANCE>.verifyJwt({ jwt })
return {
props: {
authorized: verified ? true : false
},
}
}
export default Protected
I have this page moved into it's own directory now.
"getServerSideProps" isn't supported in Next.js 13 https://beta.nextjs.org/docs/data-fetching/fundamentals. The docs say "previous Next.js APIs such as getServerSideProps, getStaticProps, and getInitialProps are not supported in the new app directory." So how would I change my code to work in Next.js 13?
P.S. I know what it looks like but this cookie IS NOT HANDLING USER AUTHENTICATION. I understand that someone could alter the cookie and gain access to the protected page. This is just a small piece of a larger app with other security mechanisms that I have in place.
import { cookies } from "next/headers";
this is next/headers.js cookie function
function cookies() {
(0, _staticGenerationBailout).staticGenerationBailout('cookies');
const requestStore = _requestAsyncStorage.requestAsyncStorage && 'getStore' in _requestAsyncStorage.requestAsyncStorage ? _requestAsyncStorage.requestAsyncStorage.getStore() : _requestAsyncStorage.requestAsyncStorage;
return requestStore.cookies;
}
this is making a request to the client side to get the cookie. In app directory, you are on the server and you can write this inside the component.
const cookie = cookies().get("cookieName")?.value
you can access to your cookie via "cookies-next" library.
pnpm i cookies-next
check this out : https://www.npmjs.com/package/cookies-next

How to Configure React Client, .NET Core 3.1 Backend, Single Tenant SSO

I'm wondering if this is possible?
I have my entire API locked behind an [Authorize] tag.
And my startup class is configured like so:
using Application.TestEntities;
using FluentValidation.AspNetCore;
using MediatR;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Persistence;
namespace API
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DataContext>(opt =>
{
opt.UseSqlite(Configuration.GetConnectionString("DefaultConnection"));
});
services.AddCors(opt =>
{
opt.AddPolicy("CorsPolicy", policy =>
{
policy.AllowAnyHeader().AllowAnyMethod().WithOrigins("http://localhost:3000");
});
});
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(opt => Configuration.Bind("AzureAd", opt));
services.AddMediatR(typeof(List.Handler).Assembly);
services.AddControllers().AddFluentValidation(cfg => cfg.RegisterValidatorsFromAssemblyContaining<Create>());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<ErrorHandlingMiddleware>();
if (env.IsDevelopment())
{
// app.UseDeveloperExceptionPage();
}
app.UseCors("CorsPolicy");
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
With my appsettings.json set up thusly:
{
"ConnectionStrings": {
"DefaultConnection": "Data source = blahblah.db"
},
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<MyTenantId>",
"ClientId": "<MyClientId>"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
On the client side of things, I'm using react-aad-msal inside my index.tsx:
import React from "react";
import ReactDOM from "react-dom";
import { Router } from "react-router-dom";
import { createBrowserHistory } from "history";
import "react-toastify/dist/ReactToastify.min.css";
import "react-widgets/dist/css/react-widgets.css";
import "./app/layout/styles.css";
import App from "./app/layout/App";
import * as serviceWorker from "./serviceWorker";
import ScrollToTop from "./app/layout/ScrollToTop";
import dateFnsLocalizer from "react-widgets-date-fns";
import { AzureAD } from "react-aad-msal";
import { authProvider } from "./app/common/util/authProvider";
new dateFnsLocalizer();
export const history = createBrowserHistory();
ReactDOM.render(
// <React.StrictMode>
<AzureAD provider={authProvider} forceLogin={true}>
<Router history={history}>
<ScrollToTop>
<App />
</ScrollToTop>
</Router>
</AzureAD>,
// </React.StrictMode>,
document.getElementById("root")
);
serviceWorker.unregister();
authProvider.ts:
import { MsalAuthProvider, LoginType } from "react-aad-msal";
const config = {
auth: {
authority:
"https://login.microsoftonline.com/<MyTenantId>",
clientId: "<MyClientId>",
redirectUri: "http://localhost:3000",
},
cache: {
cacheLocation: "localStorage" as
| "localStorage"
| "sessionStorage"
| undefined,
storeAuthStateInCookie: true,
},
};
export const authenticationParameters = {
scopes: ["<WhatDoIEvenPutHere?>"],
};
const options = {
loginType: LoginType.Redirect,
tokenRefreshUri: window.location.origin,
};
export const authProvider = new MsalAuthProvider(
config,
authenticationParameters,
options
);
The goal is to have my API and my React app locked behind Azure AD authentication, so that you would have to be on my tenant to access either of them.
The user will be redirected to the Microsoft login page via the component in the client. Upon logging in, they will gain access to the React application as well as the API endpoints.
The question is, is this possible? How to the registrations work in Azure? One registration for the client and one for the API? Or one registration that both apps reference? What about scopes? Exposing the API?
Edit:
I... think I got it?
One registration in Azure.
Authenticate as SPA with http://localhost:3000 as the redirect URI.
Check both "Access tokens" and "ID tokens" under Implicit Grant.
Expose an API with App ID URI "api://(guid)"
Add scope: name whatever you want - I used "api", so it shows up as "api://(guid)/api"
API Permissions -> Add a permission -> MY APIs -> select the application and check the scope you added previously.
And in your authProvider.ts put in the scope: ["(guid)/(YourScopeName)"], so mine is ["(guid)/api"]
It seems to be working now. In postman I can put in the bearer token that I get back from logging in, and get back data, and when I disable it, I get back a 401. So that's what I'm looking for pretty much.
To anyone reading this please let me know if I'm doing anything wrong or inadvisable.
From my edit in the original post:
One registration in Azure.
Authenticate as SPA with http://localhost:3000 as the redirect URI. Check both "Access tokens" and "ID tokens" under Implicit Grant.
Expose an API with App ID URI "api://(guid)" Add scope: name whatever you want - I used "api", so it shows up as "api://(guid)/api"
API Permissions -> Add a permission -> MY APIs -> select the application and check the scope you added previously.
And in your authProvider.ts put in the scope: ["(guid)/(YourScopeName)"], so mine is ["(guid)/api"]

What's the correct way to setup different URL prefix in production and development environment?

How do you setup different URL Prefix in production and development environment? So during development and production there are different domains that are being hit, how do you dynamically switch between two differnt hosts?
This is what I am doing right now.
environment.ts
export const environment {
production: true
}
export const DEVELOPMENT_API_PREFIX = 'http://localhost:55403/api/v1';
export const PRODUCTION_API_PREFIX = 'http://example.com/api/v1';
login.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { environment, PRODUCTION_API_PREFIX, DEVELOPMENT_API_PREFIX } from '../../environments/environment';
#Injectable({
providedIn: 'root'
})
export class LoginService {
API_PREFIX: string;
constructor(private http: HttpClient) { }
loginUser(username: string, password: string) {
if (environment.production) {
this.API_PREFIX = PRODUCTION_API_PREFIX;
} else {
this.API_PREFIX = DEVELOPMENT_API_PREFIX;
}
const formData = new FormData();
formData.append('username', username);
formData.append('password', password);
return this.http.post(this.API_PREFIX + 'login', formData);
}
}
Now the problem with this setup is, that I have to go write an IF statement in a every service. Just wanted to know which is a better way of doing things.
One of most solution is to create environment.[env].ts file for each environment. for exemple for production, create environment.prod.ts file. So you can use the same key in this new config file. For exemple:
in environment.prod.ts
API_PREFIX: 'http://xxx'
in environment.ts
API_PREFIX: 'http://xxx
Now open your angular.json file, find the configurations section then process the replacement:
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
then just import the API_PREFIX
import { environment, API_PREFIX } from '...';
You can follow this link for most explanations:
https://www.jvandemo.com/how-to-use-environment-variables-to-configure-your-angular-application-without-a-rebuild/

How can i use my custom http error msg that are in the response body in react-admin

am using laravel as Backend and react-admin as frontend .
my problem is react-admin is using default error messages based on http error code am looking for a way to use my custom messages that are generated in my backend
Take a look at Custom sagas, I use custom saga in my app to do the exact thing that you try to do, I have Laravel for backend and I want my validation errors to be displayed in React-admin,
My errorSagas.js file:
import { CRUD_CREATE_FAILURE } from "react-admin";
import { stopSubmit } from 'redux-form';
import { put, takeEvery } from "redux-saga/effects";
export default function* errorSagas() {
yield takeEvery(CRUD_CREATE_FAILURE, crudCreateFailure);
}
export function* crudCreateFailure(action) {
var json = action.payload;
// json structure looks like this:
// {
// username: "This username is already taken",
// age: "Your age must be above 18 years old"
// }
yield put(stopSubmit('record-form', json));
}
In App.js:
...
import errorSagas from './sagas/errorSagas';
...
class App extends Component {
render() {
return (
<Admin
customSagas={[errorSagas]}
...
That's it, now all of the validation errors come back to the react-admin form and are marked as the errors in the form

Authentication failure Error: The specified authentication provider is not enabled for this Firebase [duplicate]

I am creating a simple sample auth app with Ionic 2 and angularfire 2 as backend, when i try to create new user it says:
EXCEPTION: Error: Uncaught (in promise): Error: The specified
authentication provider is not enabled for this Firebase.
But i already enabled firebase authentication in firebase console:
app.ts
import {App, Platform} from 'ionic-angular';
import {StatusBar} from 'ionic-native';
import {HomePage} from './pages/home/home';
import { FIREBASE_PROVIDERS, defaultFirebase, firebaseAuthConfig, AuthProviders, AuthMethods } from 'angularfire2';
#App({
template: '<ion-nav [root]="rootPage"></ion-nav>',
providers: [
FIREBASE_PROVIDERS,
defaultFirebase('https://samplequizapp-50eb5.firebaseio.com'),
firebaseAuthConfig({
provider: AuthProviders.Password,
method: AuthMethods.Password
})
],
config: {} // http://ionicframework.com/docs/v2/api/config/Config/
})
export class MyApp {
rootPage: any = HomePage;
constructor(platform: Platform) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
StatusBar.styleDefault();
});
}
}
home.ts
import { Page } from 'ionic-angular';
import { AngularFire, FirebaseListObservable } from 'angularfire2';
import { OnInit } from '#angular/core'
#Page({
templateUrl: 'build/pages/home/home.html'
})
export class HomePage implements OnInit {
user: any = {};
data: FirebaseListObservable<any[]>;
constructor(private af: AngularFire) {
}
ngOnInit() {
this.data = this.af.database.list('/userId')
}
signUp(data) {
this.af.auth.createUser({
email: data.email,
password: data.password
})
}
}
I am pretty sure there is nothing wrong with my code:
Firebase2 in its current version (2.4.2) is not yet compatible with Firebase SDK v3, and all projects created with the new Firebase console are only accessible with calls comaptible with SDK v3.
You want to create your Firebase backend in the legacy console www.firebase.com first, and then migrate to the new console.
This is documented in this closed issue of the angularfire2 github: https://github.com/angular/angularfire2/issues/189

Resources